<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="theme-color" content="#3367D6"/>
  <link rel="apple-touch-icon" href="/icons-192.png">
  <link rel="manifest" href="/manifest.json">
  <link rel="stylesheet" href="/lib/google-code-prettify/themes/tomorrow-night.css">
  <script type="text/javascript" src="/lib/google-code-prettify/src/prettify.js"></script>
  
  <meta name="generator" content="Hexo 5.4.2">

  
    <meta name="description" content="觉宇宙之无穷，识盈虚之有数">
  

  

  
    <meta name="author" content="RocPengHua">
  

  

  

  <title>JVM从入门到精通 | 小鹏驿站</title>

  

  
    <link rel="shortcut icon" href="/favicon.ico">
  

  

  
<link rel="stylesheet" href="/css/style.css">

</head>
<body onload="PR.prettyPrint()">
  <div class="root-container">
    
<!-- header container -->
<header class="header-container post">
  
    <div class="post-image" style="background-image: url(&#39;https://api.lixingyong.com/api/images?postid=abfe342d&amp;type=url&amp;itype=image&#39;)"></div>
  
  <!-- navbar -->
<nav class="navbar">
  <div class="navbar-content">
    <!-- logo -->
    <div class="navbar-logo">
      <a href="/">
        
          小鹏驿站
        
      </a>
    </div>
    <!-- link -->
    <div class="navbar-link">
      <div class="navbar-btn">
        <div></div>
        <div></div>
        <div></div>
      </div>
      <ul class="navbar-list">
        
              <li class="navbar-list-item">
                <a href="/">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-home faa-shake" aria-hidden="true"></i>
                    首页
                  </span>
                </a>
                
              </li>
            
              <li class="navbar-list-item">
                <a href="/archives">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-archive faa-shake" aria-hidden="true"></i>
                    归档
                  </span>
                </a>
                
              </li>
            
              <li class="navbar-list-item">
                <a href="/categories">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-folder faa-shake" aria-hidden="true"></i>
                    分类
                  </span>
                </a>
                
                  <ul class="sub-menu">
                    
                      <li>
                        <a href="/categories/%E5%90%8E%E7%AB%AF/">
                          <i class="fa fa-etsy" aria-hidden="true"></i>
                          后端
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E5%89%8D%E7%AB%AF/">
                          <i class="fa fa-grav" aria-hidden="true"></i>
                          前端
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E8%BD%AF%E4%BB%B6/">
                          <i class="fa fa-key" aria-hidden="true"></i>
                          软件
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E7%AE%97%E6%B3%95/">
                          <i class="fa fa-glass" aria-hidden="true"></i>
                          算法
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/">
                          <i class="fa fa-database" aria-hidden="true"></i>
                          数据库
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/">
                          <i class="fa fa-arrows-alt" aria-hidden="true"></i>
                          中间件
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E9%97%AE%E9%A2%98/">
                          <i class="fa fa-telegram" aria-hidden="true"></i>
                          问题
                        </a>
                      </li>
                    
                      <li>
                        <a href="/categories/%E9%97%B2%E8%B0%88/">
                          <i class="fa fa-vcard" aria-hidden="true"></i>
                          闲谈
                        </a>
                      </li>
                    
                  </ul>
                
              </li>
            
              <li class="navbar-list-item">
                <a href="/tags">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-tags" aria-hidden="true"></i>
                    标签
                  </span>
                </a>
                
                  <ul class="sub-menu">
                    
                      <li>
                        <a href="/tags/%E5%8A%A0%E5%AF%86/">
                          <i class="fa fa-wpexplorer" aria-hidden="true"></i>
                          加密
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/idea/">
                          <i class="fa fa-eercast" aria-hidden="true"></i>
                          idea
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/java/">
                          <i class="fa fa-coffee" aria-hidden="true"></i>
                          java
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/mysql/">
                          <i class="fa fa-database" aria-hidden="true"></i>
                          mysql
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/oracle/">
                          <i class="fa fa-database" aria-hidden="true"></i>
                          oracle
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/spring/">
                          <i class="fa fa-leaf" aria-hidden="true"></i>
                          spring
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/redis/">
                          <i class="fa fa-fire" aria-hidden="true"></i>
                          redis
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/mongodb/">
                          <i class="fa fa-database" aria-hidden="true"></i>
                          mongodb
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/rocketmq/">
                          <i class="fa fa-random" aria-hidden="true"></i>
                          rocketmq
                        </a>
                      </li>
                    
                      <li>
                        <a href="/tags/kafka/">
                          <i class="fa fa-random" aria-hidden="true"></i>
                          kafka
                        </a>
                      </li>
                    
                  </ul>
                
              </li>
            
              <li class="navbar-list-item">
                <a href="/music">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-music" aria-hidden="true"></i>
                    音乐
                  </span>
                </a>
                
              </li>
            
              <li class="navbar-list-item">
                <a href="/links">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-link" aria-hidden="true"></i>
                    友链
                  </span>
                </a>
                
              </li>
            
              <li class="navbar-list-item">
                <a href="/about">
                  <span class="faa-parent animated-hover">
                    <i class="fa  fa-signing" aria-hidden="true"></i>
                    关于
                  </span>
                </a>
                
              </li>
            
      </ul>
    </div>
  </div>
</nav>

  
  <span class="local-search local-search-google local-search-plugin" style="left: calc(50% - 200px);top: 0px;;position:absolute;z-index:2;">
      <input type="search" placeholder="小鹏驿站" id="local-search-input" class="local-search-input-cls" style="">
      <div id="local-search-result" class="local-search-result-cls"></div>
  </span>


  
  

  
  

  
  

  
  

  
  
    <div class="header-content">
      <div class="post-text layout-block">
        <div class="layout-margin">
          <h1 class="title-wrap" title="觉宇宙之无穷，识盈虚之有数">JVM从入门到精通</h1>
          <h2 class="title-sub-wrap">
            <strong>RocPengHua</strong>
            <span>发布于</span>
            <time  class="article-date" datetime="2023-03-01T12:03:51.000Z" itemprop="datePublished">2023-03-01</time>
          </h2>
          <ul class="wrap-list dark">
  
    <li><a href="/categories/%E6%8A%80%E6%9C%AF/">📒 技术</a></li>
  
</ul>
          <ul class="wrap-list dark">
  
    <li><a href="/tags/jvm/">🏷️ jvm</a></li>
  
</ul>
        </div>
      </div>
    </div>
  

  
  
  
</header>

    <!-- 文章 -->

<!-- 文章内容 -->
<div class="body-container">
  <article class="content-container layout-block post-container">
    <div class="widget-info">
      <section class="widget-author widget-item layout-margin content-padding--primary soft-size--large soft-style--box">
  <div class="widget-body">
    
      <img src="/images/p.jpg" class="soft-size--round soft-style--box" alt="RocPengHua">
    
    
      <h2>RocPengHua</h2>
    
    
      <p>天天向上</p>
    

    <div class="count-box">
      <div class="count-box--item">
        <svg class="icon icon-article" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M240.51564747 647.74217627h196.07203239c16.59071043 0 30.16492806-13.57421762 30.16492805-30.16492806V165.10332731c0-33.18142087-30.16492806-60.32985613-60.32985612-60.32985611H245.04038668C225.43318342 104.7734712 210.35071939 119.85593522 210.35071939 139.46313845V617.57724821c0 16.59071043 13.57421762 30.16492806 30.16492808 30.16492806z m663.62841731-452.47392089v482.63884894c0 33.18142087-27.14843525 60.32985613-60.32985612 60.32985613H180.18579134c-33.18142087 0-60.32985613-27.14843525-60.32985612-60.32985613V195.26825538c-49.77213131 0-90.49478418 40.72265287-90.49478417 90.49478417v452.4739209c0 49.77213131 40.72265287 90.49478418 90.49478417 90.49478417h286.56681657c16.59071043 0 30.16492806 13.57421762 30.16492807 30.16492807s13.57421762 30.16492806 30.16492805 30.16492806h90.49478418c16.59071043 0 30.16492806-13.57421762 30.16492805-30.16492806s13.57421762-30.16492806 30.16492807-30.16492807h286.56681657c49.77213131 0 90.49478418-40.72265287 90.49478417-90.49478417V285.76303955c0-49.77213131-40.72265287-90.49478418-90.49478417-90.49478417zM587.41232014 647.74217627h191.54729318c19.60720323 0 34.68966726-15.08246403 34.68966729-34.68966727V134.93839925c0-16.59071043-13.57421762-30.16492806-30.16492808-30.16492805H617.57724821c-30.16492806 0-60.32985613 27.14843525-60.32985612 60.32985611v452.4739209c0 16.59071043 13.57421762 30.16492806 30.16492805 30.16492806z" fill="currentColor"></path>
</svg>
        <span>118</span>
      </div>
      <div class="count-box--item">
        <svg class="icon icon-categories" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M900.3614811 257.09082106h-339.81629553l-67.96326003-101.9448889c-19.41807444-29.12711113-48.54518557-43.69066667-82.52681443-43.69066667H123.6385189c-53.39970333 0-97.09036999 43.69066667-97.09037113 97.09036999v582.54222222c0 53.39970333 43.69066667 97.09036999 97.09037113 97.09037002h776.7229622c53.39970333 0 97.09036999-43.69066667 97.09037113-97.09037002V354.18119104c0-53.39970333-43.69066667-97.09036999-97.09037113-97.09036998z m-97.09036999 242.72592554H220.72888889c-24.27259221 0-48.54518557-24.27259221-48.54518556-48.54518556s24.27259221-48.54518557 48.54518556-48.54518444h582.54222222c24.27259221 0 48.54518557 24.27259221 48.54518556 48.54518444s-24.27259221 48.54518557-48.54518556 48.54518556z" fill="currentColor"></path>
</svg>
        16
      </div>
      <div class="count-box--item">
        <svg class="icon icon-tags" viewBox="0 0 1098 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M283.42180005 272q0-28.38857157-20.09142843-48.48000001t-48.47999998-20.09142842-48.48000002 20.09142842-20.09142846 48.48000001 20.09142846 48.48 48.48000002 20.09142843 48.47999998-20.09142843 20.09142843-48.48zM855.0332285 580.57142843q0 28.38857157-19.81714313 48.2057147l-263.03999997 263.58857157q-20.9142853 19.81714313-48.75428534 19.81714312-28.38857157 0-48.20571468-19.81714312l-383.04-383.58857157q-20.36571468-19.81714313-34.55999999-54.10285688t-14.19428534-62.6742853l0-222.85714313q0-27.84000002 20.36571469-48.20571469t48.2057147-20.36571466l222.85714313 0q28.38857157 0 62.6742853 14.19428529t54.65142842 34.55999999l383.04000001 382.49142843q19.81714313 20.9142853 19.81714314 48.75428532zM1060.74751475 580.57142843q0 28.38857157-19.81714313 48.2057147l-263.04 263.58857157q-20.9142853 19.81714313-48.75428531 19.81714312-19.26857155 0-31.61142843-7.47428531t-28.38857159-24.13714314l251.79428534-251.7942853q19.81714313-19.81714313 19.81714308-48.20571469 0-27.84000002-19.81714308-48.75428531l-383.04000001-382.49142845q-20.36571468-20.36571468-54.65142842-34.55999999t-62.67428532-14.19428534l120 0q28.38857157 0 62.67428532 14.19428534t54.65142842 34.55999999l383.03999998 382.49142845q19.81714313 20.9142853 19.81714314 48.75428531z" fill="currentColor"></path>
</svg>
        40
      </div>
    </div>
  </div>
</section>
      
      
      
      
<section class="widget-toc widget-item layout-margin content-padding--primary soft-size--large soft-style--box">
  <div class="widget-title">
    <svg class="icon icon-toc" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M134.50666666 767.46666668H460.8c27.73333333 0 50.24000001 22.50666668 50.24000001 50.23999999v50.13333333c0 27.73333333-22.50666668 50.24000001-50.24000001 50.24000001H134.50666666c-27.73333333 0-50.24000001-22.50666668-50.23999999-50.24000001v-50.13333333c0.10666668-27.73333333 22.50666668-50.24000001 50.24000001-50.24000001zM84.37333332 541.65333333h326.18666669c27.73333333 0 50.24000001 22.39999999 50.23999999 50.13333334v50.24000001c0 27.73333333-22.50666668 50.24000001-50.24000002 50.23999999H84.37333332c-27.73333333 0-50.24000001-22.50666668-50.23999999-50.23999999v-50.24000001c0-27.73333333 22.50666668-50.13333334 50.24000001-50.13333334zM134.50666666 315.83999999H460.8c27.73333333 0 50.24000001 22.50666668 50.24000001 50.24000001v50.24000001c0 27.73333333-22.50666668 50.13333334-50.24000001 50.13333333H134.50666666c-27.73333333 0-50.24000001-22.39999999-50.23999999-50.13333333v-50.24000001c0.10666668-27.84000001 22.50666668-50.24000001 50.24000001-50.23999999zM209.81333332 89.91999999h326.18666671c27.73333333 0 50.24000001 22.39999999 50.23999997 50.13333335v50.23999999c0 27.73333333-22.50666668 50.24000001-50.24000001 50.24000001H209.81333332c-27.73333333 0-50.24000001-22.50666668-50.23999999-50.24000001v-50.24000001c0-27.73333333 22.50666668-50.13333334 50.24000001-50.13333333zM692.05333333 623.36l274.66666669 176.00000002c23.36000001 14.93333333 30.08 45.97333334 15.14666666 69.33333332L954.77333334 910.93333333c-14.93333333 23.25333334-45.97333334 30.08-69.33333335 15.14666667l-274.66666666-176c-23.36000001-14.93333333-30.08-45.97333334-15.14666667-69.33333333l27.09333334-42.24000001c14.93333333-23.36000001 46.08000001-30.08 69.33333333-15.14666666z" fill="currentColor"></path>
</svg>
    <span>TOC</span>
  </div>
  <div class="widget-body">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#JVM%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A"><span class="toc-number">1.</span> <span class="toc-text">JVM从入门到精通</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#JVM%E4%B8%8Ejava%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84"><span class="toc-number">1.1.</span> <span class="toc-text">JVM与java体系结构</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%89%8D%E8%A8%80"><span class="toc-number">1.1.1.</span> <span class="toc-text">前言</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#java%E5%8F%91%E5%B1%95%E7%9A%84%E9%87%8D%E5%A4%A7%E4%BA%8B%E4%BB%B6"><span class="toc-number">1.1.1.1.</span> <span class="toc-text">java发展的重大事件</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E4%B8%8Ejava%E8%99%9A%E6%8B%9F%E6%9C%BA"><span class="toc-number">1.1.1.2.</span> <span class="toc-text">虚拟机与java虚拟机</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA"><span class="toc-number">1.1.1.2.1.</span> <span class="toc-text">虚拟机</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#java%E8%99%9A%E6%8B%9F%E6%9C%BA"><span class="toc-number">1.1.1.2.2.</span> <span class="toc-text">java虚拟机</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JVM%E7%9A%84%E4%BD%8D%E7%BD%AE"><span class="toc-number">1.1.1.3.</span> <span class="toc-text">JVM的位置</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JVM%E7%9A%84%E6%95%B4%E4%BD%93%E7%BB%93%E6%9E%84"><span class="toc-number">1.1.1.4.</span> <span class="toc-text">JVM的整体结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#java%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B"><span class="toc-number">1.1.1.5.</span> <span class="toc-text">java代码执行流程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JVM%E7%9A%84%E6%9E%B6%E6%9E%84%E6%A8%A1%E5%9E%8B"><span class="toc-number">1.1.1.6.</span> <span class="toc-text">JVM的架构模型</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JVM%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-number">1.1.1.7.</span> <span class="toc-text">JVM的生命周期</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%9A%84%E5%90%AF%E5%8A%A8"><span class="toc-number">1.1.1.7.1.</span> <span class="toc-text">虚拟机的启动</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%9A%84%E6%89%A7%E8%A1%8C"><span class="toc-number">1.1.1.7.2.</span> <span class="toc-text">虚拟机的执行</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%9A%84%E9%80%80%E5%87%BA"><span class="toc-number">1.1.1.7.3.</span> <span class="toc-text">虚拟机的退出</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JVM%E7%9A%84%E5%8F%91%E5%B1%95%E5%8E%86%E7%A8%8B"><span class="toc-number">1.1.1.8.</span> <span class="toc-text">JVM的发展历程</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#Sun-Classic-VM"><span class="toc-number">1.1.1.8.1.</span> <span class="toc-text">Sun Classic VM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Exact-VM"><span class="toc-number">1.1.1.8.2.</span> <span class="toc-text">Exact VM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#HotSpot-VM"><span class="toc-number">1.1.1.8.3.</span> <span class="toc-text">HotSpot VM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#JRockit"><span class="toc-number">1.1.1.8.4.</span> <span class="toc-text">JRockit</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#IBM%E7%9A%84J9"><span class="toc-number">1.1.1.8.5.</span> <span class="toc-text">IBM的J9</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#KVM%E5%92%8CCDC-x2F-CLDC-Hotspot"><span class="toc-number">1.1.1.8.6.</span> <span class="toc-text">KVM和CDC &#x2F; CLDC Hotspot</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Azul-VM"><span class="toc-number">1.1.1.8.7.</span> <span class="toc-text">Azul VM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Liquid-VM"><span class="toc-number">1.1.1.8.8.</span> <span class="toc-text">Liquid VM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Apache-Harmony"><span class="toc-number">1.1.1.8.9.</span> <span class="toc-text">Apache Harmony</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Micorsoft-JVM"><span class="toc-number">1.1.1.8.10.</span> <span class="toc-text">Micorsoft JVM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Taobao-JVM"><span class="toc-number">1.1.1.8.11.</span> <span class="toc-text">Taobao JVM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Dalvik-VM"><span class="toc-number">1.1.1.8.12.</span> <span class="toc-text">Dalvik VM</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Graal-VM"><span class="toc-number">1.1.1.8.13.</span> <span class="toc-text">Graal VM</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E5%AD%90%E7%B3%BB%E7%BB%9F"><span class="toc-number">1.2.</span> <span class="toc-text">类加载器子系统</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84%E6%A6%82%E8%BF%B0"><span class="toc-number">1.2.1.</span> <span class="toc-text">内存结构概述</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E4%B8%8E%E7%B1%BB%E7%9A%84%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B"><span class="toc-number">1.2.2.</span> <span class="toc-text">类加载器与类的加载过程</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8A%A0%E8%BD%BD%E9%98%B6%E6%AE%B5"><span class="toc-number">1.2.2.1.</span> <span class="toc-text">加载阶段</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%93%BE%E6%8E%A5%E9%98%B6%E6%AE%B5"><span class="toc-number">1.2.2.2.</span> <span class="toc-text">链接阶段</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%98%B6%E6%AE%B5"><span class="toc-number">1.2.2.3.</span> <span class="toc-text">初始化阶段</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E5%88%86%E7%B1%BB"><span class="toc-number">1.2.3.</span> <span class="toc-text">类加载器分类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E8%87%AA%E5%B8%A6%E7%9A%84%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="toc-number">1.2.3.1.</span> <span class="toc-text">虚拟机自带的类加载器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%A8%E6%88%B7%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="toc-number">1.2.3.2.</span> <span class="toc-text">用户自定义类加载器</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ClassLoader%E7%9A%84%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E"><span class="toc-number">1.2.4.</span> <span class="toc-text">ClassLoader的使用说明</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6"><span class="toc-number">1.2.5.</span> <span class="toc-text">双亲委派机制</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%85%B6%E4%BB%96"><span class="toc-number">1.2.6.</span> <span class="toc-text">其他</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%95%B0%E6%8D%AE%E5%8C%BA%E5%8F%8A%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8"><span class="toc-number">1.3.</span> <span class="toc-text">运行时数据区及程序计数器</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%95%B0%E6%8D%AE%E5%8C%BA"><span class="toc-number">1.3.1.</span> <span class="toc-text">运行时数据区</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0"><span class="toc-number">1.3.1.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B"><span class="toc-number">1.3.1.2.</span> <span class="toc-text">线程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#JVM%E7%B3%BB%E7%BB%9F%E7%BA%BF%E7%A8%8B"><span class="toc-number">1.3.1.3.</span> <span class="toc-text">JVM系统线程</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8-PC%E5%AF%84%E5%AD%98%E5%99%A8"><span class="toc-number">1.3.2.</span> <span class="toc-text">程序计数器(PC寄存器)</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88"><span class="toc-number">1.4.</span> <span class="toc-text">虚拟机栈</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E6%A6%82%E8%BF%B0"><span class="toc-number">1.4.1.</span> <span class="toc-text">虚拟机栈概述</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E5%87%BA%E7%8E%B0%E7%9A%84%E8%83%8C%E6%99%AF"><span class="toc-number">1.4.1.1.</span> <span class="toc-text">虚拟机栈出现的背景</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E6%AD%A5%E5%8D%B0%E8%B1%A1"><span class="toc-number">1.4.1.2.</span> <span class="toc-text">初步印象</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E4%B8%AD%E7%9A%84%E6%A0%88%E4%B8%8E%E5%A0%86"><span class="toc-number">1.4.1.3.</span> <span class="toc-text">内存中的栈与堆</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E5%9F%BA%E6%9C%AC%E5%86%85%E5%AE%B9"><span class="toc-number">1.4.1.4.</span> <span class="toc-text">虚拟机栈基本内容</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-number">1.4.1.4.1.</span> <span class="toc-text">java虚拟机栈是什么？</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-number">1.4.1.4.2.</span> <span class="toc-text">生命周期</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%9C%E7%94%A8"><span class="toc-number">1.4.1.4.3.</span> <span class="toc-text">作用</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A0%88%E7%9A%84%E7%89%B9%E7%82%B9"><span class="toc-number">1.4.1.4.4.</span> <span class="toc-text">栈的特点</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%9D%A2%E8%AF%95%E9%A2%98%EF%BC%9A%E5%BC%80%E5%8F%91%E4%B8%AD%E9%81%87%E5%88%B0%E5%93%AA%E4%BA%9B%E5%BC%82%E5%B8%B8%EF%BC%9F"><span class="toc-number">1.4.1.4.5.</span> <span class="toc-text">面试题：开发中遇到哪些异常？</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E7%9A%84%E5%AD%98%E5%82%A8%E5%8D%95%E5%85%83"><span class="toc-number">1.4.2.</span> <span class="toc-text">栈的存储单元</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%88%E4%B8%AD%E5%AD%98%E5%82%A8%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-number">1.4.2.1.</span> <span class="toc-text">栈中存储什么？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%88%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86"><span class="toc-number">1.4.2.2.</span> <span class="toc-text">栈运行原理</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%A0%88%E5%B8%A7%E7%9A%84%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84"><span class="toc-number">1.4.2.3.</span> <span class="toc-text">栈帧的内部结构</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8%EF%BC%88Local-Variables%EF%BC%89"><span class="toc-number">1.4.3.</span> <span class="toc-text">局部变量表（Local Variables）</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%B3%E4%BA%8ESlot-%E5%8F%98%E9%87%8F%E6%A7%BD"><span class="toc-number">1.4.3.1.</span> <span class="toc-text">关于Slot(变量槽)</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Slot%E7%9A%84%E9%87%8D%E5%A4%8D%E5%88%A9%E7%94%A8"><span class="toc-number">1.4.3.2.</span> <span class="toc-text">Slot的重复利用</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F%E5%92%8C%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E7%9A%84%E5%AF%B9%E6%AF%94"><span class="toc-number">1.4.3.3.</span> <span class="toc-text">静态变量和局部变量的对比</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A1%A5%E5%85%85%E8%AF%B4%E6%98%8E"><span class="toc-number">1.4.3.4.</span> <span class="toc-text">补充说明</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AD%97%E8%8A%82%E7%A0%81%E6%8C%87%E4%BB%A4%E8%A7%A3%E6%9E%90"><span class="toc-number">1.4.3.5.</span> <span class="toc-text">字节码指令解析</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%93%8D%E4%BD%9C%E6%95%B0%E6%A0%88"><span class="toc-number">1.4.4.</span> <span class="toc-text">操作数栈</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%A3%E7%A0%81%E8%BF%BD%E8%B8%AA"><span class="toc-number">1.4.5.</span> <span class="toc-text">代码追踪</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E9%A1%B6%E7%BC%93%E5%AD%98%E6%8A%80%E6%9C%AF%EF%BC%88Top-Of-Stack-Caching%EF%BC%89"><span class="toc-number">1.4.6.</span> <span class="toc-text">栈顶缓存技术（Top Of Stack Caching）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5%EF%BC%88Dynamic-Linking%EF%BC%89"><span class="toc-number">1.4.7.</span> <span class="toc-text">动态链接（Dynamic Linking）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E7%9A%84%E8%B0%83%E7%94%A8%EF%BC%9A%E8%A7%A3%E6%9E%90%E4%B8%8E%E5%88%86%E9%85%8D"><span class="toc-number">1.4.8.</span> <span class="toc-text">方法的调用：解析与分配</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%9D%99%E6%80%81%E9%93%BE%E6%8E%A5"><span class="toc-number">1.4.8.1.</span> <span class="toc-text">静态链接</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E9%93%BE%E6%8E%A5"><span class="toc-number">1.4.8.2.</span> <span class="toc-text">动态链接</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%97%A9%E6%9C%9F%E7%BB%91%E5%AE%9A"><span class="toc-number">1.4.8.3.</span> <span class="toc-text">早期绑定</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%99%9A%E6%9C%9F%E7%BB%91%E5%AE%9A"><span class="toc-number">1.4.8.4.</span> <span class="toc-text">晚期绑定</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%99%9A%E6%96%B9%E6%B3%95%E5%92%8C%E9%9D%9E%E8%99%9A%E6%96%B9%E6%B3%95"><span class="toc-number">1.4.8.5.</span> <span class="toc-text">虚方法和非虚方法</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%99%AE%E9%80%9A%E8%B0%83%E7%94%A8%E6%8C%87%E4%BB%A4%EF%BC%9A"><span class="toc-number">1.4.8.5.1.</span> <span class="toc-text">普通调用指令：</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E8%B0%83%E7%94%A8%E6%8C%87%E4%BB%A4%EF%BC%9A"><span class="toc-number">1.4.8.5.2.</span> <span class="toc-text">动态调用指令：</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E7%B1%BB%E5%9E%8B%E8%AF%AD%E8%A8%80%E5%92%8C%E9%9D%99%E6%80%81%E7%B1%BB%E5%9E%8B%E8%AF%AD%E8%A8%80"><span class="toc-number">1.4.8.5.3.</span> <span class="toc-text">动态类型语言和静态类型语言</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99%E7%9A%84%E6%9C%AC%E8%B4%A8"><span class="toc-number">1.4.8.6.</span> <span class="toc-text">方法重写的本质</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E7%9A%84%E8%B0%83%E7%94%A8%EF%BC%9A%E8%99%9A%E6%96%B9%E6%B3%95%E8%A1%A8"><span class="toc-number">1.4.8.7.</span> <span class="toc-text">方法的调用：虚方法表</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%96%B9%E6%B3%95%E8%BF%94%E5%9B%9E%E5%9C%B0%E5%9D%80%EF%BC%88return-address%EF%BC%89"><span class="toc-number">1.4.9.</span> <span class="toc-text">方法返回地址（return address）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%99%84%E5%8A%A0%E4%BF%A1%E6%81%AF"><span class="toc-number">1.4.10.</span> <span class="toc-text">附加信息</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E7%9A%84%E7%9B%B8%E5%85%B3%E9%9D%A2%E8%AF%95%E9%A2%98"><span class="toc-number">1.4.11.</span> <span class="toc-text">栈的相关面试题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%8E%A5%E5%8F%A3%E5%92%8C%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88"><span class="toc-number">1.5.</span> <span class="toc-text">本地方法接口和本地方法栈</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%97%B6%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%EF%BC%9F"><span class="toc-number">1.5.1.</span> <span class="toc-text">什么时本地方法？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8Native-Method"><span class="toc-number">1.5.2.</span> <span class="toc-text">为什么使用Native Method</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88"><span class="toc-number">1.5.3.</span> <span class="toc-text">本地方法栈</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%A0%86"><span class="toc-number">1.6.</span> <span class="toc-text">堆</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%EF%BC%88heap%EF%BC%89%E7%9A%84%E6%A0%B8%E5%BF%83%E6%A6%82%E8%BF%B0"><span class="toc-number">1.6.1.</span> <span class="toc-text">堆（heap）的核心概述</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E5%86%85%E5%AD%98%E7%BB%86%E5%88%86"><span class="toc-number">1.6.1.1.</span> <span class="toc-text">堆内存细分</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E7%A9%BA%E9%97%B4%E7%9A%84%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84%EF%BC%88JDK7%EF%BC%89"><span class="toc-number">1.6.1.2.</span> <span class="toc-text">堆空间的内部结构（JDK7）</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E7%A9%BA%E9%97%B4%E5%92%8C%E5%86%85%E9%83%A8%E7%BB%93%E6%9E%84%EF%BC%88JDK8%EF%BC%89"><span class="toc-number">1.6.1.3.</span> <span class="toc-text">堆空间和内部结构（JDK8）</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%BE%E7%BD%AE%E5%A0%86%E5%86%85%E5%AD%98%E5%A4%A7%E5%B0%8F%E4%B8%8EOOM"><span class="toc-number">1.6.2.</span> <span class="toc-text">设置堆内存大小与OOM</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E7%A9%BA%E9%97%B4%E5%A4%A7%E5%B0%8F%E7%9A%84%E8%AE%BE%E7%BD%AE"><span class="toc-number">1.6.2.1.</span> <span class="toc-text">堆空间大小的设置</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#OutOfMemory%E4%B8%BE%E4%BE%8B"><span class="toc-number">1.6.2.2.</span> <span class="toc-text">OutOfMemory举例</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B9%B4%E8%BD%BB%E4%BB%A3%E8%80%81%E5%B9%B4%E4%BB%A3"><span class="toc-number">1.6.3.</span> <span class="toc-text">年轻代老年代</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%BE%E8%A7%A3%E5%AF%B9%E8%B1%A1%E5%88%86%E9%85%8D%E8%BF%87%E7%A8%8B"><span class="toc-number">1.6.4.</span> <span class="toc-text">图解对象分配过程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Minor-GC-Major-GC-Full-GC"><span class="toc-number">1.6.5.</span> <span class="toc-text">Minor GC,Major GC,Full GC</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84%E5%88%86%E4%BB%A3%E5%BC%8FGC%E7%AD%96%E7%95%A5%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6"><span class="toc-number">1.6.5.1.</span> <span class="toc-text">最简单的分代式GC策略的触发条件</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%B9%B4%E8%BD%BB%E4%BB%A3GC%EF%BC%88Minor-GC%EF%BC%89%E8%A7%A6%E5%8F%91%E6%9C%BA%E5%88%B6"><span class="toc-number">1.6.5.1.1.</span> <span class="toc-text">年轻代GC（Minor GC）触发机制</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%80%81%E5%B9%B4%E4%BB%A3GC%EF%BC%88Major-GC-x2F-Full-GC%EF%BC%89%E8%A7%A6%E5%8F%91%E6%9C%BA%E5%88%B6"><span class="toc-number">1.6.5.1.2.</span> <span class="toc-text">老年代GC（Major  GC &#x2F; Full GC）触发机制</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Full-GC%E8%A7%A6%E5%8F%91%E6%9C%BA%E5%88%B6%EF%BC%9A"><span class="toc-number">1.6.5.1.3.</span> <span class="toc-text">Full GC触发机制：</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%E7%A9%BA%E9%97%B4%E5%88%86%E4%BB%A3%E6%80%9D%E6%83%B3"><span class="toc-number">1.6.6.</span> <span class="toc-text">堆空间分代思想</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5"><span class="toc-number">1.6.7.</span> <span class="toc-text">内存分配策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E5%AF%B9%E8%B1%A1%E5%88%86%E9%85%8D%E5%86%85%E5%AD%98%EF%BC%9ATLAB"><span class="toc-number">1.6.8.</span> <span class="toc-text">为对象分配内存：TLAB</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%B1TLAB%EF%BC%88Thread-Local-Allocation-Buffer%EF%BC%89"><span class="toc-number">1.6.8.1.</span> <span class="toc-text">为什么由TLAB（Thread Local Allocation Buffer）</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%97%B6TLAB"><span class="toc-number">1.6.8.2.</span> <span class="toc-text">什么时TLAB?</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#TLAB%E7%9A%84%E5%86%8D%E8%AF%B4%E6%98%8E"><span class="toc-number">1.6.8.3.</span> <span class="toc-text">TLAB的再说明</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%E7%A9%BA%E9%97%B4%E7%9A%84%E5%8F%82%E6%95%B0%E8%AE%BE%E7%BD%AE"><span class="toc-number">1.6.9.</span> <span class="toc-text">堆空间的参数设置</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%E6%97%B6%E5%88%86%E9%85%8D%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%94%AF%E4%B8%80%E9%80%89%E6%8B%A9%EF%BC%9F"><span class="toc-number">1.6.10.</span> <span class="toc-text">堆时分配对象的唯一选择？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90%E6%A6%82%E8%BF%B0"><span class="toc-number">1.6.10.1.</span> <span class="toc-text">逃逸分析概述</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90%EF%BC%9A%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96"><span class="toc-number">1.6.10.2.</span> <span class="toc-text">逃逸分析：代码优化</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A0%88%E4%B8%8A%E5%88%86%E9%85%8D"><span class="toc-number">1.6.10.2.1.</span> <span class="toc-text">栈上分配</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%90%8C%E6%AD%A5%E7%9C%81%E7%95%A5%EF%BC%88%E9%94%81%E6%B6%88%E9%99%A4%EF%BC%89"><span class="toc-number">1.6.10.2.2.</span> <span class="toc-text">同步省略（锁消除）</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A0%87%E9%87%8F%E6%9B%BF%E6%8D%A2"><span class="toc-number">1.6.10.2.3.</span> <span class="toc-text">标量替换</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90%E5%B0%8F%E7%BB%93"><span class="toc-number">1.6.10.3.</span> <span class="toc-text">逃逸分析小结</span></a></li></ol></li></ol></li></ol></li></ol>
  </div>
</section>

    </div>
  
    <div class="article-info">
      
      
      
      
      <section id="blog-content" class="article-entry markdown-body layout-margin content-padding--large soft-size--large soft-style--box">
        <blockquote>
<p>笔记来源：<a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1PJ411n7xZ">尚硅谷JVM全套教程，百万播放，全网巅峰（宋红康详解java虚拟机）</a></p>
</blockquote>
<h1 id="JVM从入门到精通"><a href="#JVM从入门到精通" class="headerlink" title="JVM从入门到精通"></a>JVM从入门到精通</h1><h2 id="JVM与java体系结构"><a href="#JVM与java体系结构" class="headerlink" title="JVM与java体系结构"></a>JVM与java体系结构</h2><h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>作为java工程师的你曾被伤害过吗？你是否也遇到过这些问题？</p>
<ul>
<li><p>运行着的线上系统突然卡死，系统无法访问，甚至直接OOM</p>
</li>
<li><p>想解决线上JVM GC问题，但却无从下手</p>
</li>
<li><p>新项目上线，对各种JVM参数设置一脸茫然，直接默认吧然后就gg了</p>
</li>
<li><p>每次面试之前都要重新背一遍JVM的一些原理概念性的东西，然而面试官却经常问你在实际项目中如何调优VM参数，如何解决GC、OOM等问题，一脸懵逼</p>
</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301200831315.png" class="asset-class" title="image-20230301200831315">

<p>大部分java开发人员，除会在项目中使用到与java平台相关的各种高精尖技术，对于java技术的核心java虚拟机了解甚少。</p>
<h4 id="java发展的重大事件"><a href="#java发展的重大事件" class="headerlink" title="java发展的重大事件"></a>java发展的重大事件</h4><ul>
<li><p>1990年，在Sun计算机公司中，由Patrick Naughton、MikeSheridan及James Gosling领导的小组Green Team，开发出的新的程序语言，命名为oak，后期命名为java</p>
</li>
<li><p>1995年，Sun正式发布java和HotJava产品，java首次公开亮相。</p>
</li>
<li><p>1996年1月23日，Sun Microsystems发布了JDK 1.0。</p>
</li>
<li><p>1998年，JDK1.2版本发布。同时，sun发布了JSP&#x2F;Servlet、EJB规范，以及将java分成了J2EE、J2SE和J2ME。这表明了java开始向企业、桌面应用和移动设备应用3大领域挺进。</p>
</li>
<li><p>2000年，JDK1.3发布，java HotSpot Virtual Machine正式发布，成为java的默认虚拟机。</p>
</li>
<li><p>2002年，JDK1.4发布，古老的Classic虚拟机退出历史舞台。</p>
</li>
<li><p>2003年年底，java平台的Scala正式发布，同年Groovy也加入了java阵营。</p>
</li>
<li><p>2004年，JDK1.5发布。同时JDK1.5改名为javaSE5.0。</p>
</li>
<li><p>2006年，JDK6发布。同年，java开源并建立了OpenJDK。顺理成章，Hotspot虚拟机也成为了openJDK中的默认虚拟机。</p>
</li>
<li><p>2007年，java平台迎来了新伙伴Clojure。</p>
</li>
<li><p>2008年，Oracle收购了BEA，得到了JRockit虚拟机。</p>
</li>
<li><p>2009年，Twitter宣布把后台大部分程序从Ruby迁移到Scala，这是java平台的又一次大规模应用。</p>
</li>
<li><p>2010年，Oracle收购了Sun，获得java商标和最真价值的HotSpot虚拟机。此时，Oracle拥有市场占用率最高的两款虚拟机HotSpot和JRockit，并计划在未来对它们进行整合：HotRockit</p>
</li>
<li><p>2011年，JDK7发布。在JDK1.7u4中，正式启用了新的垃圾回收器G1。</p>
</li>
<li><p>2017年，JDK9发布。将G1设置为默认Gc，替代CMS</p>
</li>
<li><p>同年，IBM的J9开源，形成了现在的Open J9社区</p>
</li>
<li><p>2018年，Android的java侵权案判决，Google赔偿Oracle计88亿美元</p>
</li>
<li><p>同年，Oracle宣告javaEE成为历史名词JDBC、JMS、Servlet赠予Eclipse基金会</p>
</li>
<li><p>同年，JDK11发布，LTS版本的JDK，发布革命性的ZGC，调整JDK授权许可</p>
</li>
<li><p>2019年，JDK12发布，加入RedHat领导开发的shenandoah GC</p>
</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301201223035.png" class="asset-class" title="image-20230301201223035">

<p>在JDK11之前，OracleJDK中还会存在一些OpenJDK中没有的、闭源的功能。但在JDK11中，我们可以认为OpenJDK和OracleJDK代码实质上已经完全一致的程度。</p>
<p>不过，主流的 JDK 8 在2019年01月之后就被宣布停止更新了。另外， JDK 11 及以后的版本也不再提供免费的长期支持（LTS），而且 JDK 15 和 JDK 16 也不是一个长期支持的版本，最新的 JDK 15 只支持 6 个月时间，到 2021 年 3 月，所以千万不要把 JDK 15 等非长期支持版本用在生产。</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301201317271.png" class="asset-class" title="image-20230301201317271">

<h4 id="虚拟机与java虚拟机"><a href="#虚拟机与java虚拟机" class="headerlink" title="虚拟机与java虚拟机"></a>虚拟机与java虚拟机</h4><h5 id="虚拟机"><a href="#虚拟机" class="headerlink" title="虚拟机"></a>虚拟机</h5><p>所谓虚拟机（Virtual Machine），就是一台虚拟的计算机。它是一款软件，用来执行一系列虚拟计算机指令。大体上，虚拟机可以分为<em>系统虚拟机</em>和<em>程序虚拟机</em>。</p>
<ul>
<li><p>大名鼎鼎的Visual Box，VMware就属于系统虚拟机，<em>它们完全是对物理计算机的仿真</em>，提供了一个可运行完整操作系统的软件平台。</p>
</li>
<li><p>程序虚拟机的典型代表就是java虚拟机，它<em>专门为执行单个计算机程序而设计</em>，在java虚拟机中执行的指令我们称为java字节码指令。</p>
</li>
</ul>
<p>无论是系统虚拟机还是程序虚拟机，在上面运行的软件都被限制于虚拟机提供的资源中。</p>
<h5 id="java虚拟机"><a href="#java虚拟机" class="headerlink" title="java虚拟机"></a>java虚拟机</h5><ul>
<li><p>java虚拟机是一台执行java字节码的虚拟计算机，它拥有独立的运行机制，其运行的java字节码也未必由java语言编译而成。</p>
</li>
<li><p>JVM平台的各种语言可以共享java虚拟机带来的跨平台性、优秀的垃圾回器，以及可靠的即时编译器。</p>
</li>
<li><p>java技术的核心就是java虚拟机（JVM，java Virtual Machine），因为所有的java程序都运行在java虚拟机内部。</p>
</li>
</ul>
<blockquote>
<p> 作用</p>
<ul>
<li>java虚拟机就是二进制字节码的运行环境，负责装载字节码到其内部，解释&#x2F;编译为对应平台上的机器指令执行。每一条java指令，java虚拟机规范中都有详细定义，如怎么取操作数，怎么处理操作数，处理结果放在哪里。</li>
</ul>
<p>特点</p>
<ul>
<li><p>一次编译，到处运行</p>
</li>
<li><p>自动内存管理</p>
</li>
<li><p>自动垃圾回收功能</p>
</li>
</ul>
</blockquote>
<h4 id="JVM的位置"><a href="#JVM的位置" class="headerlink" title="JVM的位置"></a>JVM的位置</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301201607575.png" class="asset-class" title="image-20230301201607575">

<p>JVM是运行在操作系统之上的，它与硬件没有直接的交互</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301201630695.png" class="asset-class" title="image-20230301201630695">

<h4 id="JVM的整体结构"><a href="#JVM的整体结构" class="headerlink" title="JVM的整体结构"></a>JVM的整体结构</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301201722582.png" class="asset-class" title="image-20230301201722582">

<ul>
<li><p>HotSpot VM是目前市面上高性能虚拟机的代表作之一。</p>
</li>
<li><p>它采用解释器与即时编译器并存的架构。</p>
</li>
<li><p>在今天，java程序的运行性能早已脱胎换骨，已经达到了可以和C&#x2F;C++程序一较高下的地步。</p>
</li>
</ul>
<h4 id="java代码执行流程"><a href="#java代码执行流程" class="headerlink" title="java代码执行流程"></a>java代码执行流程</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230301201857182.png" class="asset-class" title="image-20230301201857182">

<h4 id="JVM的架构模型"><a href="#JVM的架构模型" class="headerlink" title="JVM的架构模型"></a>JVM的架构模型</h4><p>java编译器输入的指令流基本上是一种基于<em>栈的指令集架构</em>，另外一种指令集架构则是基于<em>寄存器的指令集架构</em></p>
<p><strong>基于栈式架构的特点</strong></p>
<ul>
<li><p>设计和实现更简单，适用于资源受限的系统</p>
</li>
<li><p>避开了寄存器的分配难题：使用零地址指令方式分配</p>
</li>
<li><p>指令流中的指令大部分是零地址指令，其执行过程依赖于操作栈。指令集更小，编译器容易实现</p>
</li>
<li><p>不需要硬件支持，可移植性更好，更好实现跨平台</p>
</li>
</ul>
<p><strong>基于寄存器架构的特点</strong></p>
<ul>
<li><p>典型的应用是x86的二进制指令集：比如传统的PC以及Android的Davlik虚拟机</p>
</li>
<li><p>指令集架构则完全依赖硬件，可移植性差</p>
</li>
<li><p>性能优秀和执行更高效</p>
</li>
<li><p>花费更少的指令去完成一项操作</p>
</li>
<li><p>在大部分情况下，基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主，而基于栈式架构的指令集却是以零地址指令为主</p>
</li>
</ul>
<p><strong>举例1</strong></p>
<p>同样执行2+3这种逻辑操作，其指令分别如下：</p>
<p>基于栈的计算流程（以java虚拟机为例）:<code>反编译javap -v StackStruTest.class</code></p>
<pre><code class="prettyprint">public static void main(String[] args) &#123;
    int i = 2;
    int j = 3;
    int k = i + j;
&#125;
</code></pre>
<pre><code class="prettyprint">stack=2, locals=4, args_size=1
    0: iconst_2 //常量2入栈
    1: istore_1
    2: iconst_3 // 常量3入栈
    3: istore_2
    4: iload_1
    5: iload_2
    6: iadd //常量2/3出栈，执行相加
    7: istore_3 // 结果5入栈
    8: return
</code></pre>
<p>而基于寄存器的计算流程</p>
<pre><code class="prettyprint">mov eax,2 //将eax寄存器的值设为1
add eax,3 //使eax寄存器的值加3
</code></pre>
<p><strong>举例2</strong></p>
<pre><code class="prettyprint">public int calc()&#123;
    int a=100;
    int b=200;
    int c=300;
    return (a + b) * c;
&#125;
</code></pre>
<pre><code class="prettyprint"> Code:
      stack=2, locals=4, args_size=1
         0: bipush        100
         2: istore_1
         3: sipush        200
         6: istore_2
         7: sipush        300
        10: istore_3
        11: iload_1
        12: iload_2
        13: iadd
        14: iload_3
        15: imul
        16: ireturn
</code></pre>
<p><strong>总结</strong></p>
<p><em>由于跨平台性的设计，java的指令都是根据栈来设计的。</em>不同平台CPU架构不同，所以不能设计为基于寄存器的。优点是跨平台，指令集小，编译器容易实现，缺点是性能下降，实现同样的功能需要更多的指令。</p>
<h4 id="JVM的生命周期"><a href="#JVM的生命周期" class="headerlink" title="JVM的生命周期"></a>JVM的生命周期</h4><h5 id="虚拟机的启动"><a href="#虚拟机的启动" class="headerlink" title="虚拟机的启动"></a>虚拟机的启动</h5><p>java虚拟机的启动是通过引导类加载器（<code>bootstrap class loader</code>）创建一个初始类（<code>initial class</code>）来完成的，这个类是由虚拟机的具体实现指定的。</p>
<h5 id="虚拟机的执行"><a href="#虚拟机的执行" class="headerlink" title="虚拟机的执行"></a>虚拟机的执行</h5><ul>
<li><p>一个运行中的java虚拟机有着一个清晰的任务：执行java程序。</p>
</li>
<li><p>程序开始执行时他才运行，程序结束时他就停止。</p>
</li>
<li><p>执行一个所谓的java程序的时候，真真正正在执行的是一个叫做java虚拟机的进程。</p>
</li>
</ul>
<h5 id="虚拟机的退出"><a href="#虚拟机的退出" class="headerlink" title="虚拟机的退出"></a>虚拟机的退出</h5><ul>
<li><p>程序正常执行结束</p>
</li>
<li><p>程序在执行过程中遇到了异常或错误而异常终止</p>
</li>
<li><p>由于操作系统用现错误而导致java虚拟机进程终止</p>
</li>
<li><p>某线程调用Runtime类或system类的exit方法，或Runtime类的halt方法，并且java安全管理器也允许这次exit或halt操作。</p>
</li>
<li><p>除此之外，JNI（java Native Interface）规范描述了用JNI Invocation API来加载或卸载 java虚拟机时，java虚拟机的退出情况。</p>
</li>
</ul>
<h4 id="JVM的发展历程"><a href="#JVM的发展历程" class="headerlink" title="JVM的发展历程"></a>JVM的发展历程</h4><h5 id="Sun-Classic-VM"><a href="#Sun-Classic-VM" class="headerlink" title="Sun Classic VM"></a>Sun Classic VM</h5><ul>
<li><p>早在1996年java1.0版本的时候，Sun公司发布了一款名为sun classic VM的java虚拟机，它同时也是世界上第一款商用java虚拟机，JDK1.4时完全被淘汰。</p>
</li>
<li><p>这款虚拟机内部只提供解释器。现在还有及时编译器，因此效率比较低，而及时编译器会把热点代码缓存起来，那么以后使用热点代码的时候，效率就比较高。</p>
</li>
<li><p>如果使用JIT编译器，就需要进行外挂。但是一旦使用了JIT编译器，JIT就会接管虚拟机的执行系统。解释器就不再工作。解释器和编译器不能配合工作。</p>
</li>
<li><p>现在hotspot内置了此虚拟机。</p>
</li>
</ul>
<h5 id="Exact-VM"><a href="#Exact-VM" class="headerlink" title="Exact VM"></a>Exact VM</h5><ul>
<li><p>为了解决上一个虚拟机问题，jdk1.2时，Sun提供了此虚拟机。</p>
</li>
<li><p>Exact Memory Management：准确式内存管理</p>
</li>
<li><ul>
<li>也可以叫Non-Conservative&#x2F;Accurate Memory Management</li>
</ul>
</li>
<li><ul>
<li>虚拟机可以知道内存中某个位置的数据具体是什么类型。</li>
</ul>
</li>
<li><p>具备现代高性能虚拟机的维形</p>
</li>
<li><ul>
<li>热点探测</li>
</ul>
</li>
<li><ul>
<li>编译器与解释器混合工作模式</li>
</ul>
</li>
<li><p>只在solaris平台短暂使用，其他平台上还是classic vm</p>
</li>
<li><ul>
<li>英雄气短，终被Hotspot虚拟机替换</li>
</ul>
</li>
</ul>
<h5 id="HotSpot-VM"><a href="#HotSpot-VM" class="headerlink" title="HotSpot VM"></a>HotSpot VM</h5><ul>
<li><p>HotSpot历史</p>
</li>
<li><ul>
<li>最初由一家名为“Longview Technologies”的小公司设计</li>
</ul>
</li>
<li><ul>
<li>1997年，此公司被sun收购；2009年，Sun公司被甲骨文收购。</li>
</ul>
</li>
<li><ul>
<li>JDK1.3时，HotSpot VM成为默认虚拟机</li>
</ul>
</li>
<li><p>目前Hotspot占有绝对的市场地位，称霸武林。</p>
</li>
<li><ul>
<li>不管是现在仍在广泛使用的JDK6，还是使用比例较多的JDK8中，默认的虚拟机都是HotSpot</li>
</ul>
</li>
<li><ul>
<li>Sun &#x2F; Oracle JDK 和 OpenJDK 的默认虚拟机</li>
</ul>
</li>
<li><ul>
<li>因此本课程中默认介绍的虚拟机都是HotSpot，相关机制也主要是指HotSpot的Gc机制。（比如其他两个商用虚机都没有方法区的概念）</li>
</ul>
</li>
<li><p>从服务器、桌面到移动端、嵌入式都有应用。</p>
</li>
<li><p>名称中的HotSpot指的就是它的热点代码探测技术。</p>
</li>
<li><ul>
<li>通过计数器找到最具编译价值代码，触发即时编译或栈上替换</li>
</ul>
</li>
<li><ul>
<li>通过编译器与解释器协同工作，在最优化的程序响应时间与最佳执行性能中取得平衡</li>
</ul>
</li>
</ul>
<h5 id="JRockit"><a href="#JRockit" class="headerlink" title="JRockit"></a>JRockit</h5><ul>
<li><p>专注于服务器端应用</p>
</li>
<li><ul>
<li>它可以不太关注程序启动速度，因此JRockit内部不包含解析器实现，全部代码都靠即时编译器编译后执行。</li>
</ul>
</li>
<li><p>大量的行业基准测试显示，JRockit JVM是世界上最快的JVM。</p>
</li>
<li><ul>
<li>使用JRockit产品，客户已经体验到了显著的性能提高（一些超过了70%）和硬件成本的减少（达50%）。</li>
</ul>
</li>
<li><p>优势：全面的java运行时解决方案组合</p>
</li>
<li><ul>
<li>JRockit面向延迟敏感型应用的解决方案JRockit Real Time提供以毫秒或微秒级的JVM响应时间，适合财务、军事指挥、电信网络的需要</li>
</ul>
</li>
<li><ul>
<li>MissionControl服务套件，它是一组以极低的开销来监控、管理和分析生产环境中的应用程序的工具。</li>
</ul>
</li>
<li><p>2008年，JRockit被oracle收购。</p>
</li>
<li><p>Oracle表达了整合两大优秀虚拟机的工作，大致在JDK8中完成。整合的方式是在HotSpot的基础上，移植JRockit的优秀特性。</p>
</li>
<li><p>高斯林：目前就职于谷歌，研究人工智能和水下机器人</p>
</li>
</ul>
<h5 id="IBM的J9"><a href="#IBM的J9" class="headerlink" title="IBM的J9"></a>IBM的J9</h5><ul>
<li><p>全称：IBM Technology for java Virtual Machine，简称IT4J，内部代号：J9</p>
</li>
<li><p>市场定位与HotSpot接近，服务器端、桌面应用、嵌入式等多用途VM</p>
</li>
<li><p>广泛用于IBM的各种java产品。</p>
</li>
<li><p>目前，有影响力的三大商用虚拟机之一，也号称是世界上最快的java虚拟机。</p>
</li>
<li><p>2017年左右，IBM发布了开源J9VM，命名为openJ9，交给EClipse基金会管理，也称为Eclipse OpenJ9</p>
</li>
</ul>
<h5 id="KVM和CDC-x2F-CLDC-Hotspot"><a href="#KVM和CDC-x2F-CLDC-Hotspot" class="headerlink" title="KVM和CDC &#x2F; CLDC Hotspot"></a>KVM和CDC &#x2F; CLDC Hotspot</h5><ul>
<li><p>Oracle在java ME产品线上的两款虚拟机为：CDC&#x2F;CLDC HotSpot Implementation VM</p>
</li>
<li><p>KVM（Kilobyte）是CLDC-HI早期产品</p>
</li>
<li><p>目前移动领域地位尴尬，智能机被Android和iOS二分天下。</p>
</li>
<li><p>KVM简单、轻量、高度可移植，面向更低端的设备上还维持自己的一片市场</p>
</li>
<li><ul>
<li>智能控制器、传感器</li>
</ul>
</li>
<li><ul>
<li>老人手机、经济欠发达地区的功能手机</li>
</ul>
</li>
<li><p>所有的虚拟机的原则：一次编译，到处运行。</p>
</li>
</ul>
<h5 id="Azul-VM"><a href="#Azul-VM" class="headerlink" title="Azul VM"></a>Azul VM</h5><ul>
<li><p>前面三大“高性能java虚拟机”使用在通用硬件平台上这里Azul VW和BEA Liquid VM是与特定硬件平台绑定、软硬件配合的专有虚拟机</p>
</li>
<li><ul>
<li>高性能java虚拟机中的战斗机。</li>
</ul>
</li>
<li><p>Azul VM是Azul Systems公司在HotSpot基础上进行大量改进，运行于Azul Systems公司的专有硬件Vega系统上的java虚拟机。</p>
</li>
<li><p>每个Azul VM实例都可以管理至少数十个CPU和数百GB内存的硬件资源，并提供在巨大内存范围内实现可控的GC时间的垃圾收集器、专有硬件优化的线程调度等优秀特性。</p>
</li>
<li><p>2010年，AzulSystems公司开始从硬件转向软件，发布了自己的Zing JVM，可以在通用x86平台上提供接近于Vega系统的特性。</p>
</li>
</ul>
<h5 id="Liquid-VM"><a href="#Liquid-VM" class="headerlink" title="Liquid VM"></a>Liquid VM</h5><ul>
<li><p>高性能java虚拟机中的战斗机。</p>
</li>
<li><p>BEA公司开发的，直接运行在自家Hypervisor系统上</p>
</li>
<li><p>Liquid VM即是现在的JRockit VE（Virtual Edition），Liquid VM不需要操作系统的支持，或者说它自己本身实现了一个专用操作系统的必要功能，如线程调度、文件系统、网络支持等。</p>
</li>
<li><p>随着JRockit虚拟机终止开发，Liquid vM项目也停止了。</p>
</li>
</ul>
<h5 id="Apache-Harmony"><a href="#Apache-Harmony" class="headerlink" title="Apache Harmony"></a>Apache Harmony</h5><ul>
<li><p>Apache也曾经推出过与JDK1.5和JDK1.6兼容的java运行平台Apache Harmony。</p>
</li>
<li><p>它是IBM和Intel联合开发的开源JVM，受到同样开源的OpenJDK的压制，Sun坚决不让Harmony获得JCP认证，最终于2011年退役，IBM转而参与OpenJDK</p>
</li>
<li><p>虽然目前并没有Apache Harmony被大规模商用的案例，但是它的java类库代码吸纳进了Android SDK。</p>
</li>
</ul>
<h5 id="Micorsoft-JVM"><a href="#Micorsoft-JVM" class="headerlink" title="Micorsoft JVM"></a>Micorsoft JVM</h5><ul>
<li><p>微软为了在IE3浏览器中支持java Applets，开发了Microsoft JVM。</p>
</li>
<li><p>只能在Windows平台下运行。但确是当时Windows下性能最好的java VM。</p>
</li>
<li><p>1997年，Sun以侵犯商标、不正当竞争罪名指控微软成功，赔了Sun很多钱。微软WindowsXP SP3中抹掉了其VM。现在Windows上安装的jdk都是HotSpot。</p>
</li>
</ul>
<h5 id="Taobao-JVM"><a href="#Taobao-JVM" class="headerlink" title="Taobao JVM"></a>Taobao JVM</h5><ul>
<li><p>由AliJVM团队发布。阿里，国内使用java最强大的公司，覆盖云计算、金融、物流、电商等众多领域，需要解决高并发、高可用、分布式的复合问题。有大量的开源产品。</p>
</li>
<li><p>基于OpenJDK 开发了自己的定制版本AlibabaJDK，简称AJDK。是整个阿里java体系的基石。</p>
</li>
<li><p>基于OpenJDK Hotspot VM发布的国内第一个优化、深度定制且开源的高性能服务器版java虚拟机。</p>
</li>
<li><ul>
<li>创新的GCIH（GC invisible heap）技术实现了off-heap，即将生命周期较长的java对象从heap中移到heap之外，并且GC不能管理GCIH内部的java对象，以此达到降低GC的回收频率和提升GC的回收效率的目的。</li>
</ul>
</li>
<li><ul>
<li>GCIH中的对象还能够在多个java虚拟机进程中实现共享</li>
</ul>
</li>
<li><ul>
<li>使用crc32指令实现JVM intrinsic 降低JNI 的调用开销</li>
</ul>
</li>
<li><ul>
<li>PMU hardware 的java profiling tool 和诊断协助功能</li>
</ul>
</li>
<li><ul>
<li>针对大数据场景的ZenGc</li>
</ul>
</li>
<li><p>taobao vm应用在阿里产品上性能高，硬件严重依赖intel的cpu，损失了兼容性，但提高了性能</p>
</li>
<li><ul>
<li>目前已经在淘宝、天猫上线，把oracle官方JvM版本全部替换了。</li>
</ul>
</li>
</ul>
<h5 id="Dalvik-VM"><a href="#Dalvik-VM" class="headerlink" title="Dalvik VM"></a>Dalvik VM</h5><ul>
<li><p>谷歌开发的，应用于Android系统，并在Android2.2中提供了JIT，发展迅猛。</p>
</li>
<li><p>Dalvik VM只能称作虚拟机，而不能称作“java虚拟机”，它没有遵循 java虚拟机规范，不能直接执行java的Class文件</p>
</li>
<li><p>基于寄存器架构，不是jvm的栈架构。</p>
</li>
<li><p>执行的是编译以后的dex（Dalvik Executable）文件。执行效率比较高。</p>
</li>
<li><ul>
<li>它执行的dex（Dalvik Executable）文件可以通过class文件转化而来，使用java语法编写应用程序，可以直接使用大部分的java API等。</li>
</ul>
</li>
<li><p>Android 5.0使用支持提前编译（Ahead of Time Compilation，AoT）的ART VM替换Dalvik VM。</p>
</li>
</ul>
<h5 id="Graal-VM"><a href="#Graal-VM" class="headerlink" title="Graal VM"></a>Graal VM</h5><ul>
<li><p>2018年4月，oracle Labs公开了Graal VM，号称 “Run Programs Faster Anywhere”，野心勃勃。与1995年java的”write once，run anywhere”遥相呼应。</p>
</li>
<li><p>Graal VM在HotSpot VM基础上增强而成的跨语言全栈虚拟机，可以作为“任何语言” 的运行平台使用。语言包括：java、Scala、Groovy、Kotlin；C、C++、javascript、Ruby、Python、R等</p>
</li>
<li><p>支持不同语言中混用对方的接口和对象，支持这些语言使用已经编写好的本地库文件</p>
</li>
<li><p>工作原理是将这些语言的源代码或源代码编译后的中间格式，通过解释器转换为能被Graal VM接受的中间表示。Graal VM提供Truffle工具集快速构建面向一种新语言的解释器。在运行时还能进行即时编译优化，获得比原生编译器更优秀的执行效率。</p>
</li>
<li><p>如果说HotSpot有一天真的被取代，Graal VM希望最大。但是java的软件生态没有丝毫变化。</p>
</li>
</ul>
<blockquote>
<p>具体JVM的内存结构，其实取决于其实现，不同厂商的JVM，或者同一厂商发布的不同版本，都有可能存在一定差异。主要以Oracle HotSpot VM为默认虚拟机。</p>
</blockquote>
<h2 id="类加载器子系统"><a href="#类加载器子系统" class="headerlink" title="类加载器子系统"></a>类加载器子系统</h2><h3 id="内存结构概述"><a href="#内存结构概述" class="headerlink" title="内存结构概述"></a>内存结构概述</h3><ul>
<li>class文件</li>
<li>类加载器子系统</li>
<li>运行时数据区<ul>
<li>方法区</li>
<li>堆</li>
<li>程序计数器</li>
<li>虚拟机栈</li>
<li>本地方法栈</li>
</ul>
</li>
<li>执行引擎</li>
<li>本地方法接口</li>
<li>本地方法库</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677722582676.png" class="asset-class" width="1677722683757">

<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677722683757.png" class="asset-class" width="1677722683757">

<h3 id="类加载器与类的加载过程"><a href="#类加载器与类的加载过程" class="headerlink" title="类加载器与类的加载过程"></a>类加载器与类的加载过程</h3><p><strong>类加载子系统</strong></p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677723779525.png" class="asset-class" width="1677723779525">

<ul>
<li>类加载器子系统负责从文件系统或网络中加载class文件，class文件再文件开头有特定的文件标识</li>
<li><code>ClassLoader</code>只负责class文件的加载，至于它是否运行，则由执行引擎 <code>Execution Engine</code> 决定</li>
<li>加载的类信息存放于方法区，除类的信息外，方法区还会存放运行时常量池信息，可能还包括字符串字面量和数字字面量（这部分常量信息是 class 文件中常量池部分的内存映射 ）</li>
</ul>
<p><strong>类加载器ClassLoader角色</strong></p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677724336854.png" class="asset-class" width="1677724336854">

<ul>
<li><code>class file</code>存在本地硬盘上，可以理解为设计师画在纸上的模板，而最终这个模板在执行的时候是要加载到JVM当中来根据这个文件实例化出n个一模一样的实例</li>
<li><code>class file</code> 加载到JVM中，被称为DNA元数据模板，存放在方法区</li>
<li>在<code>.class</code>文件&gt;<code>JVM</code>&gt;最终成为元数据模板，此过程就要一个运输工具（类加载器<code>ClassLoader</code>）,扮演一个快递员的角色</li>
</ul>
<p><strong>类的加载过程</strong></p>
<pre><code class="prettyprint">public class HelloLoader &#123;
    public static void main(String[] args) &#123;
        System.out.println(&quot;Hello World!&quot;);
    &#125;
&#125;
</code></pre>
<p>用流程图表示上述示例代码：</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677724634138.png" class="asset-class" width="1677724634138">

<h4 id="加载阶段"><a href="#加载阶段" class="headerlink" title="加载阶段"></a>加载阶段</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677724652399.png" class="asset-class" width="1677724652399">

<ul>
<li>通过一个类的全限定名获取定义此类的二进制字节流</li>
<li>将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构</li>
<li>在内存汇总生成一个代表这个类的<code>java.lang.Class</code>对象，作为方法区这个类的各种数据的访问入口</li>
</ul>
<p><strong>补充：加载class文件的方式</strong></p>
<ul>
<li>从本地系统汇总直接加载</li>
<li>通过网络获取，例如：Web Applet</li>
<li>从zip压缩包中读取，成文日后jar、war格式的基础</li>
<li>运行时计算生成，动态代理技术</li>
<li>从专有数据库中提取的.class文件</li>
<li>从加密文件中获取，典型的防Class文件呗反编译的保护措施</li>
</ul>
<h4 id="链接阶段"><a href="#链接阶段" class="headerlink" title="链接阶段"></a>链接阶段</h4><p><strong>验证（Verify）:</strong></p>
<ul>
<li>目的在于确保<code>Class</code>文件的字节流中包含信息符合当前虚拟机要求，保证被加载的正确性，不会危害虚拟机自身安全</li>
<li>主要包括四种验证，<em>文件格式验证</em>、<em>元数据验证</em>、<em>字节码验证</em>、<em>符号引用验证</em></li>
</ul>
<p><strong>准备（Prepare）：</strong></p>
<ul>
<li>为类变量分配内存并且设置该类变量的默认初始值</li>
<li>这里不包含用<code>final</code>修饰的<code>static</code>,因为<code>final</code>在编译的时候就会分配，准备阶段会显示初始化</li>
<li>这里不会为实例变量分配出事话，类变量会分配在方法区中，而实例变量实惠随着对象一起分配在堆中</li>
</ul>
<p><strong>解析（Resolve）:</strong></p>
<ul>
<li>将常量池内的符号引用转换为直接引用的过程</li>
<li>事实上，解析操作往往会伴随着JVM在执行完初始化之后再执行</li>
<li>符号引用就是一组符号来描述所引用的目标，符号引用的字面量形式明确定义在《java虚拟机规范》的class文件格式中，直接引用就是直接指向目标的指针，相对偏移量或一个间接定位到目标的句柄</li>
<li>解析动作主要针对类或接口、字段、类方法、接口方法、方法类等。对应常量池中的<code>CONSTANT_Class_info</code>、<code>CONSTANT_Fieldref_inf</code>、<code>CONSTANT_Methodref_info</code>等</li>
</ul>
<h4 id="初始化阶段"><a href="#初始化阶段" class="headerlink" title="初始化阶段"></a>初始化阶段</h4><ul>
<li>初始化阶段就是执行类构造器方法<code>&lt;clinit&gt;()</code>过程</li>
<li>此方法不需要定义，是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来</li>
<li>构造器方法中指令按语句在源文件中出现的顺序执行</li>
<li><code>&lt;clinit&gt;()</code>不同于类的构造器。（构造器是虚拟机视角下的<code>&lt;init&gt;()</code>）</li>
<li>若该类具有父类，JVM会保证子类的<code>&lt;clinit&gt;()</code>执行前，父类的<code>&lt;clinit&gt;()</code>已经执行完毕</li>
<li>虚拟机必须保证一个类的<code>&lt;clinit&gt;()</code>方法子啊多线程下呗同步加锁</li>
</ul>
<h3 id="类加载器分类"><a href="#类加载器分类" class="headerlink" title="类加载器分类"></a>类加载器分类</h3><p>JVM支持两种类型的类加载器，分别为<strong>引导类加载器（Bootstrap ClassLoader）</strong>和<strong>自定义类加载器（User-Defined ClassLoader）</strong></p>
<p>自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器，但是java虚拟机规范却没有这么定义，而是将<strong>所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器</strong></p>
<p>无论类加载器的类型如果划分，在程序中我们最常见的类加载器始终只有3个，如下所示：</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677727037908.png" class="asset-class" width="1677727037908">

<p>这里的四者之间的关系是包含关系。不是上层下层，也不是子父类的继承关系。</p>
<h4 id="虚拟机自带的类加载器"><a href="#虚拟机自带的类加载器" class="headerlink" title="虚拟机自带的类加载器"></a>虚拟机自带的类加载器</h4><pre><code class="prettyprint">public class ClassLoaderTest &#123;
    public static void main(String[] args) &#123;
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        //获取其上层：扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1b6d3586
        //获取其上层:获取不到引导类加载器
        ClassLoader bootstrapClassLoader = extClassLoader.getParent();
        System.out.println(bootstrapClassLoader);//null

        //对于用户自定类类型来说:默认使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //String类使用引导类加载器进行加载的。-------&gt;java的核心类库都是使用引导类加载器进行加载的
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1); //null
    &#125;
&#125;
</code></pre>
<p><strong>启动类加载器（引导类加载器，Bootstrap ClassLoader）</strong></p>
<ul>
<li>这个类加载器使用C&#x2F;C++语言实现的，嵌套在JVM内部</li>
<li>它用来加载java的核心库（ <code>%java_HOME%\jre\lib\rt.jar</code> , <code>resources.jar</code> 或 <code>sun.boot.class.path</code> 路径下的内容），用于提供JVM自身需要的类</li>
<li>并不继承自<code>java.lang.ClassLoader</code>,没有父类加载器</li>
<li>加载扩展类加载器和应用程序类加载器，并指定为他们的父类加载器</li>
<li>出于安全考虑，<code>Bootstrap</code> 启动类加载器只加载报名为java、javax、sun等开头的类</li>
</ul>
<p><strong>扩展类加载器（Extension ClassLoader）</strong></p>
<ul>
<li>java语言编写，由<code>sun.misc.Launcher$ExtClassLoader</code>实现</li>
<li>派生于<code>ClassLoader</code>类</li>
<li>父类加载器为启动类加载器</li>
<li>从<code>java.ext.dirs</code> 系统属性所指定的目录中加载类库，或从JDK安装目录的<code>jre/lib/ext</code>子目录（扩展目录）下加载类库，如果用户创建的JAR放在此目录下，也会自动由扩展类加载器加载</li>
</ul>
<p><strong>应用程序类加载器（系统类加载器，AppClassLoader）</strong></p>
<ul>
<li>java语言编写，有<code>sun.misc.Launcher$AppClassLoader</code>实现</li>
<li>派生于<code>ClassLoader</code>类</li>
<li>父类加载器为扩展类加载器</li>
<li>它负责加载环节变量<code>classpath</code>或系统属性<code>java.class.path</code>指定路径下的类库</li>
<li>该类加载器是程序用默认的类加载器，一般来说，java应用的类都是由它完成加载</li>
<li>通过<code>java.lang.ClassLoader#getSystemClassLoader()</code>方法可以获取到该类加载器</li>
</ul>
<pre><code class="prettyprint">public class ClassLoaderTest1 &#123;
    public static void main(String[] args) &#123;
        System.out.println(&quot;*****************启动类加载器*******************&quot;);
        //获取Bootstrap ClassLoader 能够加载的api的路径
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urLs) &#123;
            System.out.println(element.toExternalForm());
        &#125;
        //从上面的路径中随意选择一个类，来看看他的类加载器是什么:引导类加载器
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);// null
        System.out.println(&quot;*****************扩展类加载器*******************&quot;);
        String extDirs = System.getProperty(&quot;java.ext.dirs&quot;);
        for (String path : extDirs.split(&quot;;&quot;)) &#123;
            System.out.println(path);
        &#125;
        //从上面的路径中随意选择一个类，来看看他的类加载器是什么:扩展类加载器
        ClassLoader classLoader1 = SunEC.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@38af3868
    &#125;
&#125;
</code></pre>
<h4 id="用户自定义类加载器"><a href="#用户自定义类加载器" class="headerlink" title="用户自定义类加载器"></a>用户自定义类加载器</h4><p>在java的日常应用程序开发中，类的加载几乎又虚拟机自带的类加载器相互配合执行的，在必要时，我们还可以自定义类加载器，来定制类的加载方式</p>
<p>为什么要自定义类加载器？</p>
<ul>
<li>隔离加载类</li>
<li>修改类的加载方式</li>
<li>扩展加载源</li>
<li>防止源码泄露</li>
</ul>
<p>用户自定义类加载器实现步骤</p>
<ul>
<li>开发人员可以通过继承抽象类<code>java.lang.ClassLoader</code> 类的方式，实现自己的类加载器，以满足一些特殊的需求</li>
<li>在JDK1.2之前，在自定义类加载器时，总会继承<code>ClassLoader</code> 类并重写<code>loadClass()</code> 方法，从而实现自定义的类加载器，但是在JDK1.2之后，已不再建议用户取覆盖<code>loadClass()</code> 方法，而是建议把自定义的类加载逻辑写在<code>findClass()</code>方法中</li>
<li>在编写自定义类加载器，如果没有太过于复杂的需求，可以直接继承<code>URLClassLoader</code>类，这样就可以避免自己去编写<code>findClass()</code>方法及其获取字节码流的方式，使自定义类加载器编写更加简洁</li>
</ul>
<pre><code class="prettyprint">public class CustomClassLoader extends ClassLoader&#123;
    @Override
    protected Class&lt;?&gt; findClass(String name) throws ClassNotFoundException &#123;
        try &#123;
            byte[] result = getClassFromCustomPath(name);
            if(result == null)&#123;
                throw new FileNotFoundException();
            &#125;else&#123;
                return defineClass(name,result,0,result.length);
            &#125;

        &#125; catch (FileNotFoundException e) &#123;
            e.printStackTrace();
        &#125;
        throw new ClassNotFoundException(name);
    &#125;

    private byte[] getClassFromCustomPath(String name) &#123;
        // 从自定义路径中加载指定类：
        // 如果指定路径的字节码文件进行了加密，则需要在此方法中进行解密操作
        return null;
    &#125;
&#125;
</code></pre>
<h3 id="ClassLoader的使用说明"><a href="#ClassLoader的使用说明" class="headerlink" title="ClassLoader的使用说明"></a>ClassLoader的使用说明</h3><p><code>ClassLoader</code>类是一个抽象类，其后所有的类加载器都继承自<code>ClassLoader</code>（不包含启动类加载器）</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677733682107.png" class="asset-class" width="1677733682107">

<p><code>sum.misc.Launcher</code>它是java虚拟机的入口应用</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677733738406.png" class="asset-class" width="1677733738406">

<p><strong>获取<code>ClassLoderde</code>的方式</strong></p>
<ul>
<li><p>获取当前的<code>ClassLoader</code></p>
<pre><code class="prettyprint">clazz.getClassLoader()
</code></pre>
</li>
<li><p>获取当前线程上下文的<code>ClassLoader</code></p>
<pre><code class="prettyprint">Thread.currentThread().getContextClassLoader()
</code></pre>
</li>
<li><p>获取系统的ClassLoader</p>
<pre><code class="prettyprint">ClassLoader.getSystemClassLoader()
</code></pre>
</li>
<li><p>获取调用者的ClassLoader</p>
<pre><code class="prettyprint">DriverManager.getCallerClassLoader()
</code></pre>
</li>
</ul>
<h3 id="双亲委派机制"><a href="#双亲委派机制" class="headerlink" title="双亲委派机制"></a>双亲委派机制</h3><p>java虚拟机对class文件采用的是按需加载的方式，也就是说当需要使用该类的时候才会将他的class文件加载到内存生成class对象。而且加载某个类的class文件时，java虚拟机采用的是双亲委派模式。即把请求交由父类加载器出来，它是一种任务委派模式</p>
<p><strong>工作原理</strong></p>
<ul>
<li>如果一个类加载器收到类加载的请求，他并不会自己先去加载，而是把这个请求委托给父类加载器去执行</li>
<li>如果父类加载器还存在其父类加载器，则进一步向上委托，依次递归，请求最终到达顶层的启动类加载器</li>
<li>如果父类加载器可以完成加载任务，就成功返回，若父类加载器无法完成此家在任务，子加载器才会尝试自己去加载</li>
</ul>
<p><strong>举例</strong></p>
<p>当我们加载<code>jdbc.jar</code> 用于实现数据库连接的时候，首先需要知道的是 <code>jdbc.jar</code> 是基于SPI接口进行实现的，所以在加载的时候，会进行双亲委派，最终从加载器加载SPI核心类，然后加载SPI接口类，接着进行反向委派，通过线程上下文类加载器进行实现类 <code>jdbc.jar</code> 的加载</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677734435180.png" class="asset-class" width="1677734435180">



<p><strong>优势</strong></p>
<ul>
<li>避免类的重复加载</li>
<li>保护程序安全，防止核心API被随意篡改<ul>
<li>自定义类：<code>java.lang.String</code></li>
<li>自定义类：<code>java.lang.ShkStart</code>(编译报错：阻止创建 <code>java.lang </code>开头的类)</li>
</ul>
</li>
</ul>
<p><strong>沙箱安全机制</strong></p>
<p>自定义 String 类，但是在加载自定义Stirng类的时候会率先使用引导类加载器加载，而引导类加载器在加载过程中会先加载jdk自带的文件（rt.jar包中的java&#x2F;lang&#x2F;String.class），报错信息说没有main方法，就是因为加载的是rt.jar包中的String类，这样可以保证对java核心源代码的保护，这就是沙箱安全机制</p>
<h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p><strong>如何判断两个class对象是否相同</strong></p>
<p>在JVM中标识两个class对象是否为同一个类存在两个必要条件</p>
<ul>
<li>类的完整类名必须一致，包括包名</li>
<li>加载这个类的<code>ClassLoader</code>（指<code>ClassLoder</code>实例对象）必须相同</li>
</ul>
<p>在JVM中，即使两个类对象（class对象）来源同一个class文件，被同一个虚拟机所加载，但只要加载她们的ClassLoader实例对象不同，那么这两个类对象也是不相同的</p>
<p><strong>对类加载器的引用</strong></p>
<p>JVM必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的，那么JVM会<strong>将这个类加载器的一个引用作为类型信息的一部分保存在方法区中</strong>。当解析一个类型到另一个类型的引用的时候，JVM需要保证这两个类型的类加载器是相同的。</p>
<p><strong>类的主动使用和被动使用</strong></p>
<p>java程序对类的使用方式分为：主动使用和被动使用。</p>
<p>主动使用，又分为七种情况：</p>
<ul>
<li>创建类的实例</li>
<li>访问某个类或接口中的静态变量，或者对该静态变量赋值</li>
<li>调用类的静态方法</li>
<li>反射（如：Class.forName(“com.rocpenghua.Test”）</li>
<li>初始化一个类的子类</li>
<li>java虚拟机启动时被标明为启动类的类</li>
<li>JDK7开始提供动态语言支持<ul>
<li><code>java.lang.invoke.MethodHandle</code> 实例的解析结果</li>
<li><code>REF_getStatic</code>、<code>REF_putStatic</code>、<code>REF_invokeStatic</code>句柄对应的类没有初始化，则初始化</li>
</ul>
</li>
</ul>
<p>除了以上七种情况，其他使用java类的方式都被看作是对<strong>类的被动使用</strong>，都<strong>不会导致类的初始化</strong>。</p>
<h2 id="运行时数据区及程序计数器"><a href="#运行时数据区及程序计数器" class="headerlink" title="运行时数据区及程序计数器"></a>运行时数据区及程序计数器</h2><h3 id="运行时数据区"><a href="#运行时数据区" class="headerlink" title="运行时数据区"></a>运行时数据区</h3><h4 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302200219087.png" class="asset-class" title="image-20230302200219087">

<p>当我们通过前面的：类的加载 -&gt; 验证 -&gt; 准备 -&gt; 解析 -&gt; 初始化 这几个阶段完成后，就会用到执行引擎对我们的类进行使用，同时执行引擎将会使用到我们运行时数据区</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302201258990.png" class="asset-class" title="image-20230302201258990">

<p>内存是非常重要的系统资源，是硬盘和CPU的中间仓库及桥梁，承载着操作系统和应用程序的实时运行，JVM内存布局规定了java在运行过程中内存申请、分配、管理的策略，保证了JVM的高校稳定运行。不同的JVM对于内存的划分方式和管理机制存在着部分差异。</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302202038832.png" class="asset-class" title="image-20230302202038832">

<p>我们通过磁盘或者网络IO得到的数据，都需要先加载到内存中，然后CPU从内存中获取数据进行读取，也就是说内存充当了CPU和磁盘之间的桥梁</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302202519412.png" class="asset-class" title="image-20230302202519412">

<p>java虚拟机定义了若干中程序运行期间会使用到的运行时数据区，其中有一些会随着虚拟机启动而创建，随着虚拟机退出而销毁，另外一些测试与线程一一对应的，这些与线程对应的数据区域会随着线程开始和结束而创建和销毁</p>
<p>灰色的为单独线程私有的，红色的为多个线程共享的。</p>
<ul>
<li>每个线程：独立包括程序计数器、栈、本地栈</li>
<li>线程间共享：堆、堆外内存（永久代或元空间、代码缓存）</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302212030169.png" class="asset-class" title="image-20230302212030169">

<p>每个JVM只有一个Runtime实例。即运行时环境，相当于内存结构的中间的运行时环境</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302212205011.png" class="asset-class" title="image-20230302212205011">

<h4 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h4><p>线程是一个程序里的运行单元，JVM允许一个应用有多个线程并行的执行，在HotSpot JVM里，每个线程都与操作系统的本地线程直接映射</p>
<p>当一个java线程准备好执行以后，此时一个操作系统的本地线程也同时创建。java线程执行中之后，本地线程也会回收</p>
<p>操作系统负责所有线程的安排调度到任何一个可用的CPU上。一旦本地线程初始化成功，它就会调用java线程中的<code>run()</code>方法</p>
<h4 id="JVM系统线程"><a href="#JVM系统线程" class="headerlink" title="JVM系统线程"></a>JVM系统线程</h4><p>如果你使用<code>jconsole</code> 或者时任何一个调试工具，都能看到后台有许多线程在运行。这些后台线程不包括调用<code>public static void main(String[] args)</code>的<code>main</code>线程自己创建的线程</p>
<p>后台系统线程在HotSpot JVM里主要是以下几个：</p>
<ul>
<li><strong>虚拟机线程：</strong>这种线程的操作是需要JVM 达到安全点才会出现，这些操作必须在不同的线程中发生的原因时他们都需要JVM达到安全点，这样堆才不会变化。这种线程的执行类型包括 <code>stop-the-world</code> 的垃圾收集，线程栈收集，线程挂起以及偏向锁撤销。</li>
<li><strong>周期任务线程：</strong>这种线程的操作是时间周期事件的体现（比如中断），他们一般用于周期性操作的调度执行</li>
<li><strong>GC线程：</strong>这种线程对在JVM里不同种类的垃圾收集行为提供支持</li>
<li><strong>编译线程：</strong>这种线程在运行时会将字节码编译成本地代码</li>
<li><strong>信号调度线程：</strong>这种线程接受信号并发送给JVM，在它内部通过调用适当的方法进行处理</li>
</ul>
<h3 id="程序计数器-PC寄存器"><a href="#程序计数器-PC寄存器" class="headerlink" title="程序计数器(PC寄存器)"></a>程序计数器(PC寄存器)</h3><p>JVM中的程序计数器（Program Counter Register）中，Register的命名源于CPU的寄存器，寄存器存储指定相关的现场信息。CPU只有把数据装载到寄存器才能运行。这里并非广义上所指的物理寄存器，或许将其翻译为PC计数器会更加贴切（也称为程序钩子），并且也不容易引起一些不必要的误会。<strong>JVM中的PC寄存器时对物理PC寄存器的一种抽象模拟</strong></p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302220920611.png" class="asset-class" title="image-20230302220920611">

<p><strong>作用</strong></p>
<p>PC寄存器用来存储指向下一条指令的地址，也即将要执行的指令代码，由执行引擎读取下一条指令</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302221030663.png" class="asset-class" title="image-20230302221030663">

<p>它是一块很小的内存空间，几乎可以忽略不计，也是<strong>运行速度最快的存储区域</strong></p>
<p>在JVM规范中，每个线程都有它自己的程序计数器，时线程私有的，生命周期和线程的生命周期保持一致</p>
<p>任何时间一个线程都只有一个方法在执行，也就是所谓的当前方法。程序计数器会存储当前线程正在执行的java方法的JVM指令地址。或者，如果是在执行native方法，则是未指定值（undefined）</p>
<p>它是程序控制流的E指示器，分支、循环、跳转、一场处理、线程恢复等基础功能都需要依赖这个计数器来完成</p>
<p>字节码解释器工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令</p>
<p>它是唯一一个在java虚拟机规范中没有规定任务OutofMemoryError情况的区域</p>
<p><strong>举例说明</strong></p>
<pre><code class="prettyprint">public int minus()&#123;
    int c = 3;
    int d = 4; 
    return c - d;
&#125;
</code></pre>
<p>字节码文件：</p>
<pre><code class="prettyprint">0: iconst_3
1: istore_1
2: iconst_4
3: istore_2
4: iload_1
5: iload_2
6: isub
7: ireturn
</code></pre>
<p><strong>使用PC寄存器存储字节码指令地址有什么用呢？为什么使用PC寄存器记录当前线程的执行地址呢？</strong></p>
<p>因为CPU需要不停的切换各个线程，这时候切换回来以后，就得知道接着从哪开始继续执行。</p>
<p>JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230302221800904.png" class="asset-class" title="image-20230302221800904">

<p><strong>PC寄存器为什么被设定为私有的？</strong></p>
<p>我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法，CPU会不停地做任务切换，这样必然导致经常中断或恢复，如何保证分毫无差呢？<strong>为了能够准确地记录各个线程正在执行的当前字节码指令地址，最好的办法自然是为每一个线程都分配一个PC寄存器</strong>，这样一来各个线程之间便可以进行独立计算，从而不会出现相互干扰的情况。</p>
<p>由于CPU时间片轮限制，众多线程在并发执行过程中，任何一个确定的时刻，一个处理器或者多核处理器中的一个内核，只会执行某个线程中的一条指令</p>
<p>这样必然导致经常中断或恢复，如何保证分毫无差呢？每个线程在创建后，都会产生自己的程序计数器和栈帧，程序计数器在各个线程之间互不影响。</p>
<p><strong>CPU时间片</strong></p>
<p>CPU时间片即CPU分配给各个程序的时间，每个线程被分配一个时间段，称作它的时间片。</p>
<p>在宏观上：我们可以同时打开多个应用程序，每个程序并行不悖，同时运行。</p>
<p>但在微观上：由于只有一个CPU，一次只能处理程序要求的一部分，如何处理公平，一种方法就是引入时间片，每个程序轮流执行。</p>
<img src="2023-03-01-JVM从入门到精通/image-20230302222015842.png" alt="image-20230302222015842"  />

<h2 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h2><h3 id="虚拟机栈概述"><a href="#虚拟机栈概述" class="headerlink" title="虚拟机栈概述"></a>虚拟机栈概述</h3><h4 id="虚拟机栈出现的背景"><a href="#虚拟机栈出现的背景" class="headerlink" title="虚拟机栈出现的背景"></a>虚拟机栈出现的背景</h4><p>由于跨平台性的设计，java的指令都是根据栈来设计的，不同平台CPU架构不同，所以不能设计为基于寄存器的。</p>
<blockquote>
<p>优点：跨平台，指令集小，编译器容易实现，缺点是性能下降，实现同样的功能需要更多的指令</p>
</blockquote>
<h4 id="初步印象"><a href="#初步印象" class="headerlink" title="初步印象"></a>初步印象</h4><p>有不少java开发人员一提到java内存结构，就会非常粗粒度地将JVM中的内存区理解为仅有java堆（<code>heap</code>）和java栈（<code>stack</code>）？为什么？</p>
<h4 id="内存中的栈与堆"><a href="#内存中的栈与堆" class="headerlink" title="内存中的栈与堆"></a>内存中的栈与堆</h4><p><strong>栈是运行时的单位，而堆是存储的单位</strong></p>
<ul>
<li>栈解决程序的运行问题，即程序如何执行，或者说如何处理数据</li>
<li>堆解决的是数据存储的问题，即数据怎么放，放的位置</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677818499466.png" class="asset-class" width="1677818499466">

<h4 id="虚拟机栈基本内容"><a href="#虚拟机栈基本内容" class="headerlink" title="虚拟机栈基本内容"></a>虚拟机栈基本内容</h4><h5 id="java虚拟机栈是什么？"><a href="#java虚拟机栈是什么？" class="headerlink" title="java虚拟机栈是什么？"></a>java虚拟机栈是什么？</h5><p>java虚拟机栈（java Virtual Machine Stack）,早期也叫java栈，每个线程在创建时都会创建一个虚拟机栈，其内部保存一个个的栈帧（Stack Frame），对应着一次次的java方法调用，是线程私有的</p>
<h5 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h5><p>虚拟机栈的生命周期和线程一致</p>
<h5 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h5><p>主管java程序的运行，它保存方法的局部变量、部分结果，并参与方法的调用和返回</p>
<h5 id="栈的特点"><a href="#栈的特点" class="headerlink" title="栈的特点"></a>栈的特点</h5><p>栈是一种快速有效的分配存储方式，访问速度仅次于程序计数器</p>
<p>JVM直接对java栈的操作只有两个：</p>
<ul>
<li>每个方法执行，伴随着进栈（入栈，压栈）</li>
<li>执行结束后的出栈工作</li>
</ul>
<p>对于栈来说不存在垃圾回收问题（栈存在溢出的情况）</p>
<h5 id="面试题：开发中遇到哪些异常？"><a href="#面试题：开发中遇到哪些异常？" class="headerlink" title="面试题：开发中遇到哪些异常？"></a>面试题：开发中遇到哪些异常？</h5><p><strong>栈中可能出现的异常</strong></p>
<p>java虚拟机规范允许<strong>java栈的大小是动态的或者是固定不变的</strong></p>
<ul>
<li>如果采用固定的大小的java虚拟机栈，那每个线程的java虚拟机栈容量可以再线程创建的时候独立选定。如果线程请求分配的栈容量超过java虚拟机栈允许的最大容量，java虚拟机将会抛出一个<code>StackOverFlowError</code>异常**(栈溢出)**</li>
<li>如果java虚拟机栈可以动态扩展，并且在尝试扩展的时候无法申请到足够的内存，或者再创建新的线程时没有足够的内存区创建对应的虚拟机栈，那java虚拟机将会抛出一个<code>OutOfMemoryError</code><strong>（堆溢出）</strong></li>
</ul>
<p>模拟栈溢出：死循环</p>
<pre><code class="prettyprint">public static void main(String[] args) &#123;
    test();
&#125;
public static void test() &#123;
    test();
&#125;
//抛出异常：Exception in thread&quot;main&quot;java.lang.StackoverflowError
//程序不断的进行递归调用，而且没有退出条件，就会导致不断地进行压栈。
</code></pre>
<p><strong>设置栈内存大小</strong></p>
<p>我们可以使用参数 <code>-Xss</code>选项来设置线程的最大栈空间，栈的大小直接决定了函数调用的最大可达深度</p>
<pre><code class="prettyprint">public class StackDeepTest&#123; 
    private static int count=0; 
    public static void recursion()&#123;
        count++; 
        recursion(); 
    &#125;
    public static void main(String args[])&#123;
        try&#123;
            recursion();
        &#125; catch (Throwable e)&#123;
            System.out.println(&quot;deep of calling=&quot;+count); 
            e.printstackTrace();
        &#125;
    &#125;
&#125;
</code></pre>
<h3 id="栈的存储单元"><a href="#栈的存储单元" class="headerlink" title="栈的存储单元"></a>栈的存储单元</h3><h4 id="栈中存储什么？"><a href="#栈中存储什么？" class="headerlink" title="栈中存储什么？"></a>栈中存储什么？</h4><p>每个线程都有自己的栈，栈中的数据都是以<strong>栈帧（Stack Frame）的格式存在</strong></p>
<p>在这个线程上正在执行的每个方法都各自对应一个<strong>栈帧（Stack Frame）</strong></p>
<p>栈帧是一个内存区块，是一个数据集，维系着方法的执行过程中的各种数据信息</p>
<h4 id="栈运行原理"><a href="#栈运行原理" class="headerlink" title="栈运行原理"></a>栈运行原理</h4><p>JVM直接对java栈的操作只有两个，就是对 <strong>栈帧的压栈和出栈，遵循“先进后出&#x2F;后进先出原则”</strong></p>
<p>在一条活动线程中，一个时间点上，只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧（栈顶栈帧）是有效的，这个栈帧被称为<strong>当前栈帧（Current Frame）</strong>，与当前栈帧相对应的方法就是<strong>当前方法（Current Method）</strong>,定义这个方法的类就是<strong>当前类（Current Class）</strong></p>
<p>执行引擎运行的所有字节码指令只针对当前栈帧进行操作</p>
<p>如果在该方法中调用了其他方法，对应的新的栈帧会被创建出来，放在栈的顶端，成为新的当前帧</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677832779628.png" class="asset-class" width="1677832779628">

<p>不同线程中所包含的栈帧是不允许存在相互引用的，即不可能在一个栈帧之中引用另一个线程的栈帧</p>
<p>如果当前方法调用了其他方法，方法返回之际，当前栈帧会传回此方法的执行结果给前一个栈帧，接着，虚拟机会丢弃当前栈帧，使得前一个栈帧重新成为当前栈帧</p>
<p>java方法有两种返回函数的方法，<strong>一种是正常的函数返回，使用return指令；另一种是抛出异常。不管使用哪种方式，都会导致栈帧被弹出</strong></p>
<pre><code class="prettyprint">public class CurrentFrameTest&#123;
    public void methodA()&#123;
        system.out.println（&quot;当前栈帧对应的方法-&gt;methodA&quot;);
        methodB();
        system.out.println（&quot;当前栈帧对应的方法-&gt;methodA&quot;);
    &#125;
    public void methodB()&#123;
        System.out.println（&quot;当前栈帧对应的方法-&gt;methodB&quot;);
    &#125;
&#125;
</code></pre>
<h4 id="栈帧的内部结构"><a href="#栈帧的内部结构" class="headerlink" title="栈帧的内部结构"></a>栈帧的内部结构</h4><ul>
<li><p><strong>局部变量表（Local Variables）</strong></p>
</li>
<li><p><strong>操作数栈（operand Stack）（表达式栈）</strong></p>
</li>
<li><p>动态链接（DynamicLinking）（指向运行时常量池的方法引用）</p>
</li>
<li><p>方法返回地址（Return Address）（方法正常退出或者异常退出的定义）</p>
</li>
<li><p>一些附加信息</p>
</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677834424127.png" class="asset-class" width="1677834424127">

<p>并行每个线程下的栈都是私有的，因此每个线程都有自己各自的栈，并且每个栈里面都有很多栈帧，栈帧的大小主要由局部变量表和操作数栈决定的</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677834531062.png" class="asset-class" width="1677834531062">

<h3 id="局部变量表（Local-Variables）"><a href="#局部变量表（Local-Variables）" class="headerlink" title="局部变量表（Local Variables）"></a>局部变量表（Local Variables）</h3><p>局部变量表也被称之为局部变量数组或本地变量表</p>
<ul>
<li><strong>定义为一个数字数组，主要用于存储方法参数和定义在方法体内的局部变量</strong>，这些数据类型包括各种类基本数据类型、对象引用（reference），一级热土人Address类型</li>
<li>由于局部变量表是建立在线程的栈上，是线程的私有数据，因此<strong>不存在数据安全问题</strong></li>
<li><strong>局部变量表所需要的容量大小是在编译期确定下来的</strong>，并保证在方法的Code属性的<code>maximum local variables</code> 数据项中。在方法运行期间是不会改变局部变量表的大小的</li>
<li><strong>方法嵌套调用的次数由栈的大小决定</strong>，一般来说，栈越大，方法嵌套调用次数越多。对一个函数而言，它的参数和局部标量越多，使得局部变量表膨胀，他的栈帧就越大，以满足方法调用所需传递的信息增大的需求。进而函数调用就会占用更多的栈空间，导致其嵌套调用次数就会减少</li>
<li><strong>局部变量表中的变量只在当前方法调用中有效。</strong>在方法执行时，虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当前方法调用结束后，随着方法栈帧的销毁，局部变量表也会随之销毁</li>
</ul>
<h4 id="关于Slot-变量槽"><a href="#关于Slot-变量槽" class="headerlink" title="关于Slot(变量槽)"></a>关于Slot(变量槽)</h4><ul>
<li>局部变量表，最基本的存储单元式Slot(变量槽)</li>
<li>参数值的存放总是在局部变量数组的<code>index0</code>开始，到数组长度-1的索引结束</li>
<li>局部变量表中存放编译期克制的各种基本数据类型（8种），引用类型（refrence），returnAddress类型的变量</li>
<li>在局部变量表里，32位以内的类型只占用一个slot（包括returnAddress类型），64位的类型（long和double）占用两个slot</li>
<li>byte、short、char在存储前被转换位int，boolean也被转换成int，0标识false，非0标识true</li>
<li>JVM会为局部变量表中的每一个Slot都分配一个访问索引，通过这个索引即可成功访问到局部变量表中指定的局部变量值</li>
<li>当一个实例方法被调用的时候，他的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上</li>
<li>如果需要访问局部变量表中一个64bit的局部变量值时，只需要使用前一个索引即可。（比如：访问long或double类型变量）</li>
<li>如果当前帧是由构造方法或者实例方法创建的，那么该对象引用this将会存放在index为0的slot处，其余的参数按照参数表顺序继续排列</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/1677837174453.png" class="asset-class" width="1677837174453">

<h4 id="Slot的重复利用"><a href="#Slot的重复利用" class="headerlink" title="Slot的重复利用"></a>Slot的重复利用</h4><p>栈帧中的局部变量表中的槽位时可以重用的，如果以讴歌局部变量过了其作用域，那么在其作用域之后申明的新的局部变量就很有可能会复用过期局部变量的槽位，从而达到节省资源的目的</p>
<pre><code class="prettyprint">public class SlotTest &#123;
    public void localVarl() &#123;
        int a = 0;
        System.out.println(a);
        int b = 0;
    &#125;
    public void localVar2() &#123;
        &#123;
            int a = 0;
            System.out.println(a);
        &#125;
        //此时的就会复用a的槽位
        int b = 0;
    &#125;
&#125;
</code></pre>
<h4 id="静态变量和局部变量的对比"><a href="#静态变量和局部变量的对比" class="headerlink" title="静态变量和局部变量的对比"></a>静态变量和局部变量的对比</h4><p>参数表分配完毕之后，再根据方体内定义的变量的顺序和作用于分配</p>
<p>我们知道类变量表有两次初始化的机会，第一次是在 <strong>准备阶段</strong>，执行系统初始化，对类变量设置零值，另一次则是在 <strong>初始化</strong>阶段，赋予程序员在代码中定义的初始值</p>
<p>和类变量初始化不同的时，局部变量表不存在系统初始化的过程，这意味着一旦定义了局部变量则必须人为的初始化，否则无法使用</p>
<pre><code class="prettyprint">public void test()&#123;
    int i;
    System. out. println(i);
&#125;
// 这样的代码是错误的，没有赋值不能使用
</code></pre>
<h4 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h4><p>在栈帧中，与性能调优关系最为密切的部分就是前面提到的局部变量表。在方法执行时，虚拟机使用局部变量表完成方法的传递</p>
<p><strong>局部变量表中的变量也是重要的垃圾回收根节点，只要被局部变量表中直接或间接引用的对象都不会被回收</strong></p>
<h4 id="字节码指令解析"><a href="#字节码指令解析" class="headerlink" title="字节码指令解析"></a>字节码指令解析</h4><pre><code class="prettyprint">public class LocalVariablesTest &#123;
    private int count = 0;

    public static void main(String[] args) &#123;
        LocalVariablesTest test = new LocalVariablesTest();
        int num = 10;
        test.test1();
    &#125;
    public static void testStatic()&#123;
        LocalVariablesTest test = new LocalVariablesTest();
        Date date = new Date();
        int count = 10;
        System.out.println(count);
    &#125;
    public void test1() &#123;
        Date date = new Date();
        String name1 = &quot;rocpenghua&quot;;
        String info = test2(date, name1);
        System.out.println(date + name1);
    &#125;

    public String test2(Date datep, String name2) &#123;
        datep = null;
        name2 = &quot;rocpenghua.com&quot;;
        double weight = 130.3;
        char gender = &#39;男&#39;;
        return datep + name2;
    &#125;
    public void test3() &#123;
        count++;
    &#125;
    public void test4() &#123;
        int a = 0;
        &#123;
            int b = 0;
            b = a + 1;
        &#125;
        int c = a + 1;
    &#125;
&#125;
</code></pre>
<p>这里分析main方法：(安装idea插件<code>jclasslib</code>)</p>
<pre><code class="prettyprint"> public static void main(String[] args) &#123;
     LocalVariablesTest test = new LocalVariablesTest();
     int num = 10;
     test.test1();
 &#125;
// javap -v LocalVariablesTest
public static void main(java.lang.String[]);	// 方法名main 
    descriptor: ([Ljava/lang/String;)V		 	// 参数String数组，返回值void
    flags: ACC_PUBLIC, ACC_STATIC				// 访问标志：public static
    Code:
      stack=2, locals=3, args_size=1			// 栈的最大深度2，局部变量最大槽数3，参数个数1
         0: new           #3                  // class com/rocpenghua/java1/LocalVariablesTest
         3: dup
         4: invokespecial #4                  // Method &quot;&lt;init&gt;&quot;:()V
         7: astore_1
         8: bipush        10
        10: istore_2
        11: aload_1
        12: invokevirtual #5                  // Method test1:()V
        15: return								// 字节码长度16（0-15）
      LineNumberTable:							// 行号表：java代码行号对应字节码指令的位置
        line 14: 0								// 14: LocalVariablesTest test = new LocalVariablesTest(); -&gt; 0: new
        line 15: 8								// 15: int num = 10; -&gt; 8: bipush 10
        line 16: 11								// 16: test.test1(); -&gt; 11: aload_1
        line 17: 15
      LocalVariableTable:						// 局部变量表
        Start  Length  Slot  Name   Signature	// 起始PC(变量声明下一个位置，开始作用的位置)，变量使用长度，槽位，变量名，变量描述符
            0      16     0  args   [Ljava/lang/String; //变量开始作用位置 0: new  使用长度 16
            8       8     1  test   Lcom/rocpenghua/java1/LocalVariablesTest; //变量开始作用位置 8: new  使用长度 8
           11       5     2   num   I			//变量开始作用位置 11: new  使用长度 5
</code></pre>
<p>这里分析test4方法有代码块功能</p>
<pre><code class="prettyprint">public void test4() &#123;
    int a = 0;
    &#123;
        int b = 0;
        b = a + 1;
    &#125;
    // 变量c使用之前已经销毁的变量b的槽位
    int c = a + 1;
&#125; 
/************************************************************************/
public void test4();					// 方法名
    descriptor: ()V						// 参数空 返回值void
    flags: ACC_PUBLIC					//方法修饰符 public
    Code:
      stack=2, locals=3, args_size=1	//栈深度2，本地变量槽3
         0: iconst_0
         1: istore_1
         2: iconst_0
         3: istore_2
         4: iload_1
         5: iconst_1
         6: iadd
         7: istore_2
         8: iload_1
         9: iconst_1
        10: iadd
        11: istore_2
        12: return
      LineNumberTable:
        line 44: 0
        line 46: 2
        line 47: 4
        line 49: 8
        line 50: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            4       4     2     b   I			//变量b在代码块声明没有被继续使用，卡槽占用销毁
            0      13     0  this   Lcom/rocpenghua/java1/LocalVariablesTest;
            2      11     1     a   I
           12       1     2     c   I
</code></pre>
<h3 id="操作数栈"><a href="#操作数栈" class="headerlink" title="操作数栈"></a>操作数栈</h3><p>每个独立的栈帧除了包含局部变量表以外，还包含一个后进先出（<code>Lost-In-First-Out</code>）的操作数栈，也可以称之为 <strong>表达式栈（<code>Expression Stack</code>）</strong></p>
<p>操作数栈，在方法执行过程中，根据字节码指令往栈中写入数据或提取数据，即入栈和出栈</p>
<ul>
<li>某些字节码指令压入操作数栈，其余的字节码指令将操作数取出栈。使用它们后再把结果压入栈</li>
<li>比如：执行复制、交换、求和等操作</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230305104832218.png" class="asset-class" title="image-20230305104832218">

<p>代码举例</p>
<pre><code class="prettyprint">public void testAddOperation()&#123;
    byte i = 15; 
    int j = 8; 
    int k = i + j;
&#125;
</code></pre>
<p>字节码指令信息</p>
<pre><code class="prettyprint">public void testAddOperation(); 
    Code:
    0: bipush 15
    2: istore_1 
    3: bipush 8
    5: istore_2 
    6:iload_1 
    7:iload_2 
    8:iadd
    9:istore_3 
    10:return
</code></pre>
<p>操作数栈。主要用于保护计算过程的中间结果，同时作为计算过程中变量临时的存储空间</p>
<p>操作数栈就是JVM执行引擎的一个工作区，当一个方法刚开始执行的时候，一个新的栈帧也会随之呗创建出来，这个方法的操作数栈是空的</p>
<p>每个操作数栈都会用有一个明确的栈深度用于存储数值，其所需的最大深度在编译器就定义好了，保存在方法的Code属性中，为max_stack的值</p>
<p>栈中的任何一个元素都是可以任意的java数据类型</p>
<ul>
<li>32bit的类型占用一个栈单位深度</li>
<li>64bit的类型占用两个栈单位深度</li>
</ul>
<p>操作数栈并非采用访问索引的方式来进行数据访问的，而是只能通过标准的入栈和出栈从安坐来完成一次数据访问</p>
<p>如果被调用的方法带有返回值的话，其返回值将会呗压入当前栈帧的操作数栈中，并更新PC寄存器中的下一条需要执行的字节码指令</p>
<p>操作数栈中元素的数据类型必须与字节码指令的序列严格匹配，这由编译器在编译期间进行验证，同时在类加载过程中的类检验阶段的数据流分析阶段要再次验证</p>
<p>另外，我们说java虚拟机的解释引擎时基于栈的执行引擎，其中栈指的就是操作数栈</p>
<h3 id="代码追踪"><a href="#代码追踪" class="headerlink" title="代码追踪"></a>代码追踪</h3><pre><code class="prettyprint">public void testAddOperation() &#123;
    byte i = 15;
    int j = 8;
    int k = i + j;
&#125;
</code></pre>
<p>使用javap 命令反编译class文件：<code>javap -v 类名.class</code></p>
<pre><code class="prettyprint">public void testAddoperation(); 
        Code:
    0: bipush 15 
    2: istore_1 
    3: bipush 8
    5: istore_2
    6: iload_1
    7: iload_2
    8: iadd
    9: istore_3
    10: return
</code></pre>
<center class="half">
    <img src="2023-03-01-JVM从入门到精通/image-20230307221031312.png" width="49%" height="250px" />
    <img src="2023-03-01-JVM从入门到精通/image-20230307221112127.png" width="49%" height="250px" />
</center>
<center class="half">
    <img src="2023-03-01-JVM从入门到精通/image-20230307221130019.png" width="49%" height="250px" />
    <img src="2023-03-01-JVM从入门到精通/image-20230307221304696.png" width="49%" height="250px" />
</center>
<center class="half">
    <img src="2023-03-01-JVM从入门到精通/image-20230307221325692.png" width="49%" height="250px" />
    <img src="2023-03-01-JVM从入门到精通/image-20230307221338832.png" width="49%" height="250px" />
</center>
<center class="half">
    <img src="2023-03-01-JVM从入门到精通/image-20230307221345546.png" width="49%" height="250px" />
    <img src="2023-03-01-JVM从入门到精通/image-20230307221412377.png" width="49%" height="250px" />
</center>

<pre><code class="prettyprint">// i++和++i的区别

public void add()&#123;
    int i1 = 10; 	// bipush 10 / istore_1
    i1++;		 	// iinc 1,1 单独++没有区别
    int i2 = 10; 	// bipush 10 / istore_2
    ++i2;		 	// iinc 2,1 单独++没有区别

    int i3 = 10; 	// bipush 10 / istore_3
    int i4 = i3++; 	//iload_3 / iinc 3,1 / istore 4 先加载数据再+1 10

    int i5 = 10;	// bipush 10 / istore 5
    int i6 = ++i4; 	// iinc  4, 1 /  iload 4 / istore 6 先+1再加载数据 11

    int i7 = 10;	// bipush 10 /istore 7
    i7 = i7++;		// iload 7 / iinc 7, 1 / istore 7 先加载数据再+1再保存在同一个局部变量表位置 10

    int i8 = 10;	// bipush 10 / istore 8
    i8 = ++i8;		// iinc 8, 1 /iload 8 /istore 8 先+1再加载数据再保存在同一个局部变量表位置 11

    int i9 = 10;	// bipush 10 / istore 9
    int i10 = i9++ + ++i9;	// iload 9 / iinc 9, 1 / iinc 9, 1 / iload 9 / iadd / istore 10 先   +1，+1再加载数据，在执行add然后保存局部变量表 10 + 12 = 22
&#125;
Code:
      stack=2, locals=11, args_size=1
         0: bipush        10
         2: istore_1
         3: iinc          1, 1
         6: bipush        10
         8: istore_2
         9: iinc          2, 1
        12: bipush        10
        14: istore_3
        15: iload_3
        16: iinc          3, 1
        19: istore        4
        21: bipush        10
        23: istore        5
        25: iinc          4, 1
        28: iload         4
        30: istore        6
        32: bipush        10
        34: istore        7
        36: iload         7
        38: iinc          7, 1
        41: istore        7
        43: bipush        10
        45: istore        8
        47: iinc          8, 1
        50: iload         8
        52: istore        8
        54: bipush        10
        56: istore        9
        58: iload         9
        60: iinc          9, 1
        63: iinc          9, 1
        66: iload         9
        68: iadd
        69: istore        10
        71: return
</code></pre>
<h3 id="栈顶缓存技术（Top-Of-Stack-Caching）"><a href="#栈顶缓存技术（Top-Of-Stack-Caching）" class="headerlink" title="栈顶缓存技术（Top Of Stack Caching）"></a>栈顶缓存技术（Top Of Stack Caching）</h3><p>基于栈式架构的虚拟机所使用的令地址指令更加紧凑，但完成一项操作的时候必然需要使用更多的入栈和出栈指令，这同时也就意味着讲将需要更多的指令分派（instruction dispatch）次数和内存读&#x2F;写次数</p>
<p>由于操作数时存储在内存中的，因此频繁地执行内存读&#x2F;写操作必然会影响执行速度，为了解决这个问题，HotSport JVM的设计者们提出了栈顶缓存技术，<strong>将栈顶元素全部换存在物理CPU的寄存器中，以此降低对内存的读&#x2F;写次数，提升执行引擎的执行效率</strong></p>
<h3 id="动态链接（Dynamic-Linking）"><a href="#动态链接（Dynamic-Linking）" class="headerlink" title="动态链接（Dynamic Linking）"></a>动态链接（Dynamic Linking）</h3><p>动态链接、方法返回地址、附加信息：有些地方被称为帧数据区</p>
<p>每一个栈帧内部都包含一个指向<strong>运行时常量池中该栈帧所属方法的引用</strong>。包含这个引用的目的就是为了支持当前方式的代码能够实现动态链接（Dynamic Linking）。比如：invoke dynamic指令</p>
<p>在java源文件呗编译到字节码文件中时，所有的变量和方法引用都作为符号引用（Symbolic Reference）保存在class文件的常量池中。比如：描述一个方法调用了另外的其他方法时，就是通过常量池指向方法的符号引用来表示的，<strong>那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用</strong></p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230308203319950.png" class="asset-class" title="image-20230308203319950">

<p>为什么需要运行时常量池？</p>
<p>常量池的作用：就是为了提供一些符号和常量，便于指令的识别</p>
<h3 id="方法的调用：解析与分配"><a href="#方法的调用：解析与分配" class="headerlink" title="方法的调用：解析与分配"></a>方法的调用：解析与分配</h3><p>在JVM中，将符号引用转换为调用方法的直接引用与方法的绑定机制相关</p>
<h4 id="静态链接"><a href="#静态链接" class="headerlink" title="静态链接"></a>静态链接</h4><p>当一个字节码文件被装载进JVM内部时，如果被调用的 <strong>目标方法在编译器可知，且运行期保持不变时</strong>，这种情况下降调用方法的符号引用转换为直接引用的过程，称之为静态链接</p>
<h4 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h4><p>如果<strong>被调用的方法在编译器无法被确定下来，只能够在程序运行期将调用方法的符号转换为直接引用</strong>，由于这种引用转换过程具备动态性，因此也被称之为动态链接</p>
<p><strong>静态链接和动态链接不是名词，而是动词</strong></p>
<h4 id="早期绑定"><a href="#早期绑定" class="headerlink" title="早期绑定"></a>早期绑定</h4><p>早期绑定就是指被调用的目标方法如果在编译期可知，且运行期保持不变，即可讲这个方法与所属的类型进行绑定，这样一来，由于明确了被调用的目标方法究竟时哪一个，因此也就可以使用静态链接的方式将符号引用转换为直接饮用</p>
<h4 id="晚期绑定"><a href="#晚期绑定" class="headerlink" title="晚期绑定"></a>晚期绑定</h4><p>如果被调用的方法在编译期无法被确定下来，只能够在程序运行期根据实际的类型绑定相关的方法，这中绑定方式也就被称之为晚期绑定</p>
<p>随着高级语言的横空出世，类似于java一样的基于面向对象的编程语言如今越来越多，尽管这类编程语言在语法风格上存在一定的差别，但是他们彼此之间始终保持着一个共性，那就是都支持封装、继承和多态等，面向对象特性，既然这一类编程语言具备多态特性，那么自然也就具备早期绑定和晚期绑定两种绑定方式</p>
<p>java中任何一个普通的方法其实都是具备虚函数的特征，他们相当于C语言中的虚函数。如果java程序中不希望某个方法拥有虚函数的特征，则可以受用关键字final来标记这个方案</p>
<h4 id="虚方法和非虚方法"><a href="#虚方法和非虚方法" class="headerlink" title="虚方法和非虚方法"></a>虚方法和非虚方法</h4><p>如果方法在编译期就确定了具体的调用版本，这个版本在运行时是不可变的。这样的方法称为非虚方法</p>
<p>静态方法、似有方法、final方法、实例构造器、非类方法都是非虚方法，其他方法称为虚方法</p>
<p>在类加载的解析阶段就可以进行解析，如下时非虚方法举例：</p>
<pre><code class="prettyprint">class Father&#123;
    public static void print(String str)&#123;
        System. out. println(&quot;father &quot;+str); 
    &#125;
    private void show(String str)&#123;
        System. out. println(&quot;father&quot;+str);
    &#125;
&#125;
class Son extends Father&#123;
    public class VirtualMethodTest&#123;
        public static void main(String[] args)&#123;
            Son.print(&quot;coder&quot;);
            //Father fa=new Father();
            //fa.show(&quot;atguigu.com&quot;);
        &#125;
    &#125;
&#125;
</code></pre>
<p>虚拟机中提供了一下几条方法调用指令：</p>
<h5 id="普通调用指令："><a href="#普通调用指令：" class="headerlink" title="普通调用指令："></a>普通调用指令：</h5><ul>
<li>invokestatic：调用静态方法，解析阶段确定唯一方法版本</li>
<li>invokespecial：调用方法、私有及父类方案吗解析阶段确定唯一方法版本</li>
<li>invokevirtual：调用所有的虚方法</li>
<li>invokeinterface：调用借口方法</li>
</ul>
<pre><code class="prettyprint">class Father &#123;
    public Father() &#123;
        System.out.println(&quot;father的构造器&quot;);
    &#125;

    public static void showStatic(String str) &#123;
        System.out.println(&quot;father &quot; + str);
    &#125;

    public final void showFinal() &#123;
        System.out.println(&quot;father show final&quot;);
    &#125;

    public void showCommon() &#123;
        System.out.println(&quot;father 普通方法&quot;);
    &#125;
&#125;

public class Son extends Father &#123;
    public Son() &#123;
        super();
    &#125;

    public Son(int age) &#123;
        this();
    &#125;

    /**
     * 不是重写父类方法，静态方法不能不重写
     *
     * @param str
     */
    public static void showStatic(String str) &#123;
        System.out.println(&quot;son &quot; + str);
    &#125;

    private void showPrivate(String str) &#123;
        System.out.println(&quot;son private &quot; + str);
    &#125;

    public void show() &#123;
        //invokestatic
        showStatic(&quot;rocpenghua&quot;);
        //invokestatic
        super.showStatic(&quot;good&quot;);
        // invokespecial
        showPrivate(&quot;hello&quot;);
        //invokespecial
        super.showCommon();
        //invokevirtual
        /*
         * 因为此方法声明final不会被自雷重写，因此也认为此方法时非虚方法
         */
//        showFinal();
        //invokespecial
        super.showFinal();
        //invokevirtual
        showCommon();
        //invokevirtual
        info();

        MethodInterface in = null;
        //invokeinterface
        in.methodA();
    &#125;

    public void info() &#123;

    &#125;

    public void display(Father f) &#123;
        f.showCommon();
    &#125;

    public static void main(String[] args) &#123;
        Son son = new Son();
        son.show();
    &#125;
&#125;

interface MethodInterface &#123;
    public void methodA();
&#125;
</code></pre>
<h5 id="动态调用指令："><a href="#动态调用指令：" class="headerlink" title="动态调用指令："></a>动态调用指令：</h5><ul>
<li><code>invokedynamic</code>：动态解析出需要调用的方法，然后执行(lambda表达式)</li>
</ul>
<p>前四条指定固化在虚拟机内部，方法的调用执行不可人为干预，而invokedynamic指令则支持由用户确定方法版本。<strong>其中invokestatic指令和invokespecial指令调用的方法称为非虚方法，其余的（final修饰方法除外）称为虚方法</strong></p>
<p><strong>关于invokedynamic指令</strong></p>
<ul>
<li>JVM字节码指令集一直比较稳定，一直到java7中增加了一个<code>invokedynamic</code>指令，这是java为了实现【动态类型语言】支持而坐的一种改进</li>
<li>但是java7中没有提供直接生成invokedynamic指令的方法，需要借助ASM这种底层字节码工具来产生<code>invokedynamic</code>指令。知道java8的lambda表达式的出现，<code>invokedynamic</code>指令的生成，在java中才有了直接的生成方式</li>
<li>java7中增加的动态语言类型支持的本质是对java虚拟机规范的修改，而不是对java语言规则的修改，这一块相对来讲比较复杂，增加了虚拟机中的方法调用，最直接的受益者就是运行在java平台的动态语言的编译器。</li>
</ul>
<h5 id="动态类型语言和静态类型语言"><a href="#动态类型语言和静态类型语言" class="headerlink" title="动态类型语言和静态类型语言"></a>动态类型语言和静态类型语言</h5><p>动态类型语言和静态类型语言两者的区别在于对类型的检查时在编译期还是在运行期，满足前者就是静态类型过语言，反之是动态类型语言</p>
<p>说的再直白一点就是，静态类型语言是判断变量自身的类型信息；动态类型语言是判断变量值的类型信息，变量没有类型信息，变量值才有类型信息，这是动态原因的重要特征</p>
<h4 id="方法重写的本质"><a href="#方法重写的本质" class="headerlink" title="方法重写的本质"></a>方法重写的本质</h4><p><strong>java语言种方法重写的本质</strong></p>
<ol>
<li>找到操作数栈顶的第一个元素所执行的对象的实际类型，记作C。</li>
<li>如果在类型C中找到与常量中的描述符合简单名称都相符的方法，则进行访问权限校验，如果通过则返回这个方法的直接引用，查找过程结束；如果不通过，则返回java.lang.IllegalAccessError 异常。</li>
<li>否则，按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。</li>
<li>如果始终没有找到合适的方法，则抛出java.1ang.AbstractMethodsrror异常。</li>
</ol>
<p><strong>IllegalAccessError介绍</strong></p>
<p>程序试图访问或修改一个属性或调用一个方法，这个属性或方法，你没有权限访问。一般的，这个会引起编译器异常。这个错误如果发生在运行时，就说明一个类发生了不兼容的改变。</p>
<h4 id="方法的调用：虚方法表"><a href="#方法的调用：虚方法表" class="headerlink" title="方法的调用：虚方法表"></a>方法的调用：虚方法表</h4><p>在面向对象的编程中，会很频繁的使用到动态分派，如果在每次动态分派的过程中都要重新再类的方法元数据中搜索合适的目标的话可能影响到执行效率。因此，为了提高性能，JVM采用在类的方法去建立一个虚方法表（非虚方法不会出现在表中）来实现。使用索引表来代替查找</p>
<p>每个类中都有一个虚方法表，表中存放着各个方法的实际入口</p>
<blockquote>
<p>虚方法表是什么时候被创建的？</p>
<p>虚方法表会在类加载的链接阶段被创建并开始初始化，类的变量初始值准备完成后，JVM会把该类的方法表也初始化完毕</p>
</blockquote>
<p>举例1：</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230310205140006.png" class="asset-class" title="image-20230310205140006">

<p>举例2：</p>
<pre><code class="prettyprint">interface Friendly&#123;
    void sayHello();
    void sayGoodbye(); 
&#125;
class Dog&#123;
    public void sayHello()&#123;
    &#125;
    public String tostring()&#123;
        return &quot;Dog&quot;;
    &#125;
&#125;
class Cat implements Friendly &#123;
    public void eat() &#123;
    &#125;
    public void sayHello() &#123; 
    &#125; 
    public void sayGoodbye() &#123;
    &#125;
    protected void finalize() &#123;
    &#125;
&#125;
class CockerSpaniel extends Dog implements Friendly&#123;
    public void sayHello() &#123; 
        super.sayHello();
    &#125;
    public void sayGoodbye() &#123;
    &#125;
&#125;
</code></pre>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230310205231864.png" class="asset-class" title="image-20230310205231864">

<h3 id="方法返回地址（return-address）"><a href="#方法返回地址（return-address）" class="headerlink" title="方法返回地址（return address）"></a>方法返回地址（return address）</h3><p>存放调用该方法的PC寄存器的值，一个方法的结束，有两种方式：</p>
<ul>
<li>正常执行完成</li>
<li>出现未处理的异常，非正常退出</li>
</ul>
<p>无论哪种方式退出，在方法退出后都返回到该方法被调用的地方。方法正常退出时，<strong>调用者的PC寄存器的值作为返回地址，即调用该方法的指令的下一条指令的地址</strong>。而通过异常退出时，返回地址是要通过异常表来确定，栈帧中一般不会保存这部分信息</p>
<p>当一个方法开始执行后，只有两种方式可以退出这个方法：</p>
<ul>
<li>执行引擎遇到任意一个方法返回的字节码指令（return），会有返回值传递给上层的方法调用者，简称<strong>正常完成出口</strong><ul>
<li>一个方法在正常调用完成之后，究竟需要使用哪一个返回指令，还需要根据方法返回值的实际数据类型而定</li>
<li>在字节码指令中，返回指令包含<code>ireturn</code>(当返回值时boolean,byte,char,short,int类型时)，<code>lreturn</code>(Long类型)，<code>freturn</code>(Float类型)，<code>dreturn</code>(double类型)，areturn。另外还有一个return指令声明为<code>void</code>的方法，实例初始化方法，类和接口的初始化方式使用</li>
</ul>
</li>
<li>在方法执行过程中遇到一场（Exception）,并且这个异常没有在方法内进行处理，也就是只要再本方法的异常表中没有搜索到匹配的一场处理器，就会导致方法退出，简称<strong>异常完成出口</strong></li>
</ul>
<p>方法执行过程中，抛出异常时的异常处理，存储字一个异常处理表，方便在发生异常的时候找到处理异常的代码</p>
<p>本质上，方法的退出就是当前栈帧出栈的过程，此时，需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈，设置PC寄存器值等，让调用者方法继续执行下去</p>
<p><strong>正常完成出口和异常完成出口的区别在于：通过异常完成出口退出的不会给他的上层调用者产生任何的返回值</strong></p>
<h3 id="附加信息"><a href="#附加信息" class="headerlink" title="附加信息"></a>附加信息</h3><p>栈帧中还允许携带与java虚拟机实现相关的一些附加信息。例如：对程序调试提供支持的信息</p>
<h3 id="栈的相关面试题"><a href="#栈的相关面试题" class="headerlink" title="栈的相关面试题"></a>栈的相关面试题</h3><ul>
<li>举例栈溢出的情况？（<code>StackOverflowError</code>）<ul>
<li>通过 <code>-Xss</code> 设置栈的大小</li>
</ul>
</li>
<li>调整栈的大小，就能保证不出现溢出吗?<ul>
<li>不能保证不溢出（死递归）</li>
</ul>
</li>
<li>分配的栈内存越大越好吗？<ul>
<li>不是，一定时间内降低了OOM概率，但是会挤占其他的线程空间，因为整个空间时有限的</li>
</ul>
</li>
<li>垃圾回收是否涉及打虚拟机栈？<ul>
<li>不会</li>
</ul>
</li>
<li>方法中定义的局部变量是否线程安全？<ul>
<li>具体问题具体分析，如果对象时在内部产生，并在内部消亡，没有返回到外部，那么就是线程安全的，反之则是线程不安全的</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>运行时数据区</th>
<th>是否存在Error</th>
<th>是否存在GC</th>
</tr>
</thead>
<tbody><tr>
<td>程序计数器</td>
<td>否</td>
<td>否</td>
</tr>
<tr>
<td>虚拟机栈</td>
<td>是（StackOverflowError）</td>
<td>否</td>
</tr>
<tr>
<td>本地方法栈</td>
<td>是</td>
<td>否</td>
</tr>
<tr>
<td>方法区</td>
<td>是（OutofMemoryException）</td>
<td>是</td>
</tr>
<tr>
<td>堆</td>
<td>是</td>
<td>是</td>
</tr>
</tbody></table>
<h2 id="本地方法接口和本地方法栈"><a href="#本地方法接口和本地方法栈" class="headerlink" title="本地方法接口和本地方法栈"></a>本地方法接口和本地方法栈</h2><h3 id="什么时本地方法？"><a href="#什么时本地方法？" class="headerlink" title="什么时本地方法？"></a>什么时本地方法？</h3><p><strong>一个Native Method是一个java调用非java代码的接口</strong>。一个native method是这样一个java方法：该方法的实现由非java语言实现，比如C。</p>
<p>在定义一个native method 时，并不提供实现体（有些像定义一个java interface），因为其实现体是由非java语言在外面实现的</p>
<p>本地接口的作用是融合不同的编程语言为java所用，它的初衷是融合C&#x2F;C++程序</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230310212030884.png" class="asset-class" title="image-20230310212030884">

<pre><code class="prettyprint">public class IHaveNatives&#123;
    public native void methodNative1(int x);
    public native static long methodNative2();
    private native synchronized float methodNative3(Object o);
    native void methodNative4(int[] ary) throws Exception;
&#125;
</code></pre>
<p>标识符native可以与其它java标识符连用，但是abstract除外</p>
<h3 id="为什么使用Native-Method"><a href="#为什么使用Native-Method" class="headerlink" title="为什么使用Native Method"></a>为什么使用Native Method</h3><p><strong>与java环境的交互</strong></p>
<p><strong>有时java应用需要与java外面的环境交互，这是本地方法存在的主要原因。</strong>你可以想象java需要与一些底层系统，如操作系统或某些硬件交换信息的情况。本地方法正是这样一种交流机制，它为我们提供了一个非常简洁的接口，而且我们无需去了解java应用之外的繁琐的细节</p>
<p><strong>与操作系统的交互</strong></p>
<p>JVM支持着java语言本身和运行时库，它是java程序赖以生存的平台，它由一个解释器（解释字节码）和一些连接到本地代码的库组成。然而不管怎样，它毕竟不是一个完整的系统，它经常依赖于一底层系统的支持。这些底层系统常常是强大的操作系统。通<strong>过使用本地方法，我们得以用java实现了jre的与底层系统的交互，甚至JVM的一些部分就是用c写的</strong>。还有，如果我们要使用一些java语言本身没有提供封装的操作系统的特性时，我们也需要使用本地方法。</p>
<p><strong>Sun&#96;s java</strong></p>
<p><strong>Sun的解释器是用C实现的，这使得它能像一些普通的C一样与外部交互。</strong>jre大部分是用java实现的，它也通过一些本地方法与外界交互。例如：类java.lang.Thread的setPriority()方法是用java实现的，但是它实现调用的是该类里的本地方法setPriority()。这个本地方法是用C实现的，并被植入JVM内部，在Windows 95的平台上，这个本地方法最终将调用Win32 setPriority() ApI。这是一个本地方法的具体实现由JVM直接提供，更多的情况是本地方法由外部的动态链接库（external dynamic link library）提供，然后被JVw调用。</p>
<p><strong>现状</strong></p>
<p><strong>目前该方法使用的越来越少了，除非是与硬件有关的应用，</strong>比如通过java程序驱动打印机或者java系统管理生产设备，在企业级应用中已经比较少见。因为现在的异构领域间的通信很发达，比如可以使用Socket通信，也可以使用Web Service等等。</p>
<h3 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h3><p><strong>java虚拟机栈于管理java方法的调用，而本地方法栈用于管理本地方法的调用</strong></p>
<p>本地方法栈，也是线程私有的</p>
<p>允许被实现成固定或者是可动态扩展的内存大小。（在内存溢出方面是相同的）</p>
<ul>
<li>如果线程请求分配的栈容量超过本地方法栈允许的最大容量，java虚拟机将会抛出一个StackOverflowError异常</li>
<li>如果本地方法栈可以动态扩展，并且尝试扩展的时候无法申请到足够的内存，或者在创建新的线程时没有足够的内存去创建对应的本地方法栈，那么java虚拟机将会跑出OutOfMemoryError异常</li>
</ul>
<p>本地方法是使用C语言实现的</p>
<p>它的具体做法是Native Method Stack 中等级native方法，在Execution Engine 执行时加载本地方法库</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230310213158656.png" class="asset-class" title="image-20230310213158656">

<p><strong>当某个线程调用一个本地方法时，他就进入了一个全新的并且不再受虚拟机限制的世界，他和虚拟机拥有同样的权限</strong></p>
<ul>
<li>本地方法可以通过本地方法接口来 <strong>访问虚拟机内部的运行时数据区</strong></li>
<li>它甚至可以直接使用本地处理器中的寄存器</li>
<li>直接从本地内存中堆中分配任意数量的内存</li>
</ul>
<p>并不是所有的JVM都支持本地方法，因为java虚拟机规范并没有明确要求本地方法栈的使用语言、具体实现方式、数据结构等。如果JVM产品不打算支持native方法，也可以无需实现本地方法栈</p>
<p>在HotSpot JVM中，直接将本地方法栈和虚拟机栈合二为一</p>
<h2 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h2><h3 id="堆（heap）的核心概述"><a href="#堆（heap）的核心概述" class="headerlink" title="堆（heap）的核心概述"></a>堆（heap）的核心概述</h3><p>对针对一个JVM进程来说时唯一的，也就是一个进程只有一个JVM，但是进程包含多个线程，他们时共享同一堆空间。</p>
<p>一个JVM实例只存在一个堆内存，对也是java内存管理的核心区域</p>
<p>java堆区在JVM启动的时候即被创建，其空间大小也就确定了，时JVM管理的最大一块内存空间</p>
<ul>
<li>堆内存的大小是可以调节的</li>
</ul>
<p>《java虚拟机规范》规定，堆可以处于物理上不连续的内存空间中，但在逻辑上它应该被视为连续的。</p>
<p>所有的线程共享java堆，在这里还可以划分线程私有的缓冲区（Thread Local Allocation Buffer,TLAB）</p>
<p>《java虚拟机规范》中对java堆的描述是：所有的对象实例以及数组都应当在运行时分配在堆上。（<code>The heap is the run-time data area from which memory for all class instances and arrays is allocated</code>）</p>
<p>数组和对象可能永远不会存储在栈上，因为栈帧中保存引用，这个引用指向对象数组在堆中的位置。</p>
<p>在方法结束后，堆中的对象不会马上被移除，仅仅在垃圾回收的时候才会被移除</p>
<p>堆时GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230311180924718.png" class="asset-class" title="image-20230311180924718">

<h4 id="堆内存细分"><a href="#堆内存细分" class="headerlink" title="堆内存细分"></a>堆内存细分</h4><p>java7及之前堆内存逻辑上分为三部分：新生代+老年代+<strong>永久代</strong></p>
<ul>
<li><code>Young Generation Space</code> 新生区 <code>Young/New</code> 又被划分为<code>Eden</code>区和<code>Survivor</code>区</li>
<li><code>Tenure generation space</code> 老年代<code>Old/Tenure</code></li>
<li><code>Permanent Space</code> 永久代 <code>Perm</code></li>
</ul>
<p>java8及之后对逻辑上分为三部分：新生代+老年代+<strong>元空间</strong></p>
<ul>
<li><code>Young Generation Space</code> 新生区 <code>Young/New</code> 又被划分为<code>Eden</code>区和<code>Survivor</code>区</li>
<li><code>Tenure generation space</code> 老年代<code>Old/Tenure</code></li>
<li><code>Meta Space</code> 元空间 <code>Meta</code></li>
</ul>
<h4 id="堆空间的内部结构（JDK7）"><a href="#堆空间的内部结构（JDK7）" class="headerlink" title="堆空间的内部结构（JDK7）"></a>堆空间的内部结构（JDK7）</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230311182146416.png" class="asset-class" title="image-20230311182146416">

<h4 id="堆空间和内部结构（JDK8）"><a href="#堆空间和内部结构（JDK8）" class="headerlink" title="堆空间和内部结构（JDK8）"></a>堆空间和内部结构（JDK8）</h4><img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230311182227026.png" class="asset-class" title="image-20230311182227026">

<h3 id="设置堆内存大小与OOM"><a href="#设置堆内存大小与OOM" class="headerlink" title="设置堆内存大小与OOM"></a>设置堆内存大小与OOM</h3><h4 id="堆空间大小的设置"><a href="#堆空间大小的设置" class="headerlink" title="堆空间大小的设置"></a>堆空间大小的设置</h4><p>java堆区用于存储java对象实例，那么堆的大小在JVM启动时就已经设定好了，可以通过 <code>-Xmx</code>和 <code>-Xms</code>来设置</p>
<ul>
<li><code>-Xmx</code>：用于表示堆区的最大内存，等价于 <code>-XX:MaxHeapSize</code></li>
<li><code>-Xms</code>：用于表示堆的起始内存，等价于 <code>-XX:InitialHeapSize</code></li>
</ul>
<p>一旦堆区中的内存大小超过 <code>-Xmx</code>所指定的最大内存时，将会跑出OutOfMemoryError异常</p>
<p>通常会将<code>-Xms</code>和<code>-Xmx</code>两个参数配置相同的值，其目的是<strong>为了能够在java垃圾回收机制清理完堆区后不需要重新分割计算堆区的大小，从而提高性能</strong></p>
<blockquote>
<p>默认情况</p>
<ul>
<li>初始内存大小：物理电脑内存大小的1&#x2F; 64</li>
<li>最大内存大小：物理电脑内存大小的1&#x2F;4</li>
</ul>
</blockquote>
<h4 id="OutOfMemory举例"><a href="#OutOfMemory举例" class="headerlink" title="OutOfMemory举例"></a>OutOfMemory举例</h4><pre><code class="prettyprint">public class OOMTest &#123;
    public static void main(String[]args)&#123;
        ArrayList&lt;Picture&gt; list = new ArrayList&lt;&gt;();
        while(true)&#123;
            try &#123;
                Thread.sleep(20);
            &#125; catch (InterruptedException e)&#123;
                e.printStackTrace();
            &#125;
            list.add(new Picture(new Random().nextInt(1024*1024)));
        &#125;
    &#125;
&#125;
public class Picture &#123;
    private byte[] size;
    public Picture(int nextInt) &#123;
        this.size = new byte[nextInt];
    &#125;
&#125;
/**/
Exception in thread &quot;main&quot; java.lang.OutOfMemoryError: java heap space
    at com.rocpenghua.java.Picture.&lt;init&gt;(Picture.java:11)
    at com.rocpenghua.java.OOMTest.main(OOMTest.java:15)
</code></pre>
<h3 id="年轻代老年代"><a href="#年轻代老年代" class="headerlink" title="年轻代老年代"></a>年轻代老年代</h3><p>存储在JVM对象可以被划分为两类</p>
<ul>
<li>一类是生命周期较短的瞬时对象，这类对象的创建和消亡都非常迅速</li>
<li>另一类对象的生命周期却非常长，在某些极端的情况下还能够与JVM的生命周期保持一致</li>
</ul>
<p>java堆区进一步细分可以划分为年轻代我（YoungGen）和老年代（OldGen）</p>
<p>其中年轻代又可以划分为Eden区、Survivor0(s0)和Survivor1(s1)（有时也叫做from区和to区）</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230311223139143.png" class="asset-class" title="image-20230311223139143">

<p>下面者参数开发中一般不会调整：</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230311223223234.png" class="asset-class" title="image-20230311223223234">

<p>配置新生代与老年代在对结构的占比</p>
<ul>
<li>默认 <code>-XX:NewRatio=2</code>,表示新生代占1，老年代占2，新生代占整个堆的1&#x2F;3</li>
<li>可以修改 <code>-XX:NewRatio=4</code>,表示新生代占1，老年代占4，新生代占整个堆的1&#x2F;5</li>
</ul>
<p>在HotSpot中Eden区和另外两个Survivor区默认占比时8:1:1</p>
<p>当然开发人员可以通过配置调整 <code>-XX:SurvivorRatio</code> 调整这个空间的比例：</p>
<p>​	<code>-XX:SurvivorRatio=8 </code>：8:1:1</p>
<p>​    <code>-XX:SurvivorRatio=4</code>：4:1:1</p>
<p>几乎所有的java对象都是在Eden区被new出来的，绝大部分的java对象的销毁都在新生代进行</p>
<p>可以使用选项 <code>-Xmn</code>设置新生代最大内存大小，这个参数一般使用默认值就可以</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230311223853295.png" class="asset-class" title="image-20230311223853295">

<h3 id="图解对象分配过程"><a href="#图解对象分配过程" class="headerlink" title="图解对象分配过程"></a>图解对象分配过程</h3><p>微信对象分配内存是一件非常严谨和复杂的任务，JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题，并且由于内存分配算法与内存回收算法密切相关，所以还需要考虑GC执行完内存回收是否会在内存空间中产生内存碎片</p>
<ol>
<li>new的对象先放Eden区，此区有大小限制</li>
<li>当Eden区的空间填满时，程序有需要创建对象，JVM的垃圾回收器将对Eden区进行垃圾回收（Minor GC）,将Eden区中的不再被其他对象所引用的对象进行销毁，再加载新的对象放到Eden区</li>
<li>然后将Eden区中的剩余对象移动到Survivor0区</li>
<li>如果再次触发垃圾回收，此时幸存下来的放到Survivor0区，如果没有回收，就会放到Survivor1区</li>
<li>如果再次经历垃圾回收，此时会重新放回Survivor0区，接着再去Survivor1区</li>
<li>什么时候可以区老年代？（可以设置次数，默认15次）<ul>
<li><strong>可以设置参数：<code>-XX:MaxTenuringThreshold=N</code></strong></li>
</ul>
</li>
<li>在老年代相对悠闲，当老年代内存不足时，再次触发GC（Major GC），进行老年代的内存清理</li>
<li>若老年代进行Major GC之后，发现依然无法进行对象的保存，就会产生OOM异常</li>
</ol>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230312110307352.png" class="asset-class" title="image-20230312110307352">

<p><strong>流程图</strong></p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230312110334456.png" class="asset-class" title="image-20230312110334456">

<blockquote>
<p><strong>总结</strong></p>
<ul>
<li>针对s0,s1区的总结：复制之后由交换，谁空谁是to区</li>
<li>关于垃圾回收：频繁在新生代收集，很少在老年代收集，几乎不再永久代或元空间进行收集</li>
</ul>
</blockquote>
<p><strong>常用调优工具</strong></p>
<ul>
<li><p>JDK命令行</p>
</li>
<li><p>Eclipse:Memory Analyzer Tool</p>
</li>
<li><p>Jconsole</p>
</li>
<li><p>VisualVM</p>
</li>
<li><p>Jprofiler</p>
</li>
<li><p>java Flight Recorder</p>
</li>
<li><p>GCViewer</p>
</li>
<li><p>GC Easy</p>
</li>
</ul>
<h3 id="Minor-GC-Major-GC-Full-GC"><a href="#Minor-GC-Major-GC-Full-GC" class="headerlink" title="Minor GC,Major GC,Full GC"></a>Minor GC,Major GC,Full GC</h3><p>JVM在进行GC时，并非每次都对上面三个内存区域一起回收的，大部分时候回收的都是指新生代</p>
<p>针对HotSpot JVM的实现，它里面的GC按照回收区域分为两大种类型：一种是部分收集（Partial GC）,一种时整堆收集（Full GC）</p>
<ul>
<li>部分收集：不是完整收集整个java堆的垃圾收集。其中又分为：<ul>
<li>新生代收集（Minor GC &#x2F; Young GC）:只是新生代的垃圾收集</li>
<li>老年代收集（Major GC &#x2F; Old GC）：只是老年代的垃圾收集<ul>
<li>目前只有CMS GC会有单独收集老年代的行为</li>
<li>很多时候Major GC会和 Full GC 混淆使用，需要具体分辨是老年代回收还是整堆回收</li>
</ul>
</li>
<li>混合收集（Mixed GC）：收集整个新生代以及部分老年代的垃圾收集<ul>
<li>目前只有G1 GC会有这种行为</li>
</ul>
</li>
<li>整堆收集（Full GC）：收集整个java堆和方法区的垃圾收集</li>
</ul>
</li>
</ul>
<h4 id="最简单的分代式GC策略的触发条件"><a href="#最简单的分代式GC策略的触发条件" class="headerlink" title="最简单的分代式GC策略的触发条件"></a>最简单的分代式GC策略的触发条件</h4><h5 id="年轻代GC（Minor-GC）触发机制"><a href="#年轻代GC（Minor-GC）触发机制" class="headerlink" title="年轻代GC（Minor GC）触发机制"></a>年轻代GC（Minor GC）触发机制</h5><ul>
<li>当年轻代空间不足时，就会触发Minor GC，这里的年轻代满指的时Eden区满，Survivor满不会引发GC（每次Minor GC会清理年轻代的内存）</li>
<li>因为java对象大多都具备朝生夕灭的特性，所以Minor GC非常频繁，用户线程才恢复运行</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230312111608133.png" class="asset-class" title="image-20230312111608133">

<h5 id="老年代GC（Major-GC-x2F-Full-GC）触发机制"><a href="#老年代GC（Major-GC-x2F-Full-GC）触发机制" class="headerlink" title="老年代GC（Major  GC &#x2F; Full GC）触发机制"></a>老年代GC（Major  GC &#x2F; Full GC）触发机制</h5><ul>
<li>指发生在老年代的GC，对象从老年代消失时，我们说 <code>Major GC</code> 或 <code>Full GC</code>发生了</li>
<li>出现了Major GC，经常会伴随至少一次的Minor GC（但非绝对的，Paralle 1 Scavenge 收集器的收集策略里就有直接进行Major GC的策略选择过程）<ul>
<li>也就是在老年代空间不足时，会先尝试触发Minor GC，如果之后空间还不足，则触发Major GC</li>
</ul>
</li>
<li>Major GC的速度一般比Minor GC慢10倍以上，STW的时间更长</li>
<li>如果Major GC后内存还不足，就报OOM异常</li>
</ul>
<h5 id="Full-GC触发机制："><a href="#Full-GC触发机制：" class="headerlink" title="Full GC触发机制："></a>Full GC触发机制：</h5><ul>
<li>调用System.gc()时，系统建议执行Full GC，但是不是必然执行</li>
<li>老年代空间不足时</li>
<li>方法区空间不足时</li>
<li>通过Minor GC后进入老年代的平均大小大于老年代的可用内存</li>
<li>由Eden区、s0(From)区向s1(To)区复制时，对象大小大于 To Space可用内存，则把该对象转存到老年代，切老年代的可用内存小鱼该对象大小</li>
</ul>
<p><strong>ps：Full GC是开发或调优中尽量要避免的，这样暂停时间会短一些</strong></p>
<h3 id="堆空间分代思想"><a href="#堆空间分代思想" class="headerlink" title="堆空间分代思想"></a>堆空间分代思想</h3><p>为什么要把java堆分代？不分代就不能正常工作了吗？</p>
<p>进研究，不同对象的生命周期不同。70%-99%的对象时临时对象</p>
<ul>
<li>新生代：有Eden、两块大小相同的Survivor（又称from&#x2F;to，s0&#x2F;s1）构成，to总为空</li>
<li>老年代：存放新生代中经历多次GC仍然存活的对象</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230312112948375.png" class="asset-class" title="image-20230312112948375">

<p>其实不分代完全可以，分代的唯一理由就是优化GC性能。如果没有分代，那所有的对象都在一块，就如同把一个学校的人都放在一个教师。GC的时候找到哪些对象没用，这样就会对堆的所有区域进行扫描，而很多对象都是朝生夕死的，如果分代的话，把新创建的对象放到某个地方，当GC的时候先把这块存储朝生夕死对象的区域进行回收，这样就会腾出很大的空间出来</p>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230312113252543.png" class="asset-class" title="image-20230312113252543">

<h3 id="内存分配策略"><a href="#内存分配策略" class="headerlink" title="内存分配策略"></a>内存分配策略</h3><p>如果对象在Eden出生并经过第一次Minor GC后仍然存活，并且能被Survivor容纳的话，将被移动到Survivor空间中，并将对象的年龄设为1，对象在Survivor区中每熬过一次Minor GC，年龄就增加1岁，当它的年龄增加到一定程度（默认15岁，每个JVM每个GC都有所不同）时，就会被晋升到老年代</p>
<p>对象晋升老年代和年龄阀值，可以通过选项 <code>-XX:MaxTenuringThreshold</code> 来设置</p>
<p>针对不同年龄段的对象分配原则如下所示：</p>
<ul>
<li>优先分配到Eden</li>
<li>大对象直接分配到老年代（尽量避免程序汇总出现过多的多对象）</li>
<li>长期存活的对象分配到老年代</li>
<li>动态对象的年龄判断：如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一般，年龄大于或等于该年龄的对象可以直接进入老年代，无需等到<code>MaxTenuringShreold</code> 中要求的年龄</li>
<li>空间分配担保： <code>-XX:HandlePromotionFailure</code></li>
</ul>
<h3 id="为对象分配内存：TLAB"><a href="#为对象分配内存：TLAB" class="headerlink" title="为对象分配内存：TLAB"></a>为对象分配内存：TLAB</h3><h4 id="为什么由TLAB（Thread-Local-Allocation-Buffer）"><a href="#为什么由TLAB（Thread-Local-Allocation-Buffer）" class="headerlink" title="为什么由TLAB（Thread Local Allocation Buffer）"></a>为什么由TLAB（Thread Local Allocation Buffer）</h4><ul>
<li>堆区时线程共享区域，任何线程都可以访问到堆区中的共享数据</li>
<li>由于对象实例的创建在JVM中非常频繁，因此在并发环境下从堆区中划分内存空间是线程不安全的</li>
<li>为避免多个线程操作同一地址，需要使用加锁机制，进而影响分配速度</li>
</ul>
<h4 id="什么时TLAB"><a href="#什么时TLAB" class="headerlink" title="什么时TLAB?"></a>什么时TLAB?</h4><ul>
<li>从内存模型而不是垃圾收集的角度，堆Eden区继续进行划分，JVM为<strong>每个线程分配了一个私有缓存区域</strong>，它包含在Eden空间内</li>
<li>多线程同时分配内存时，使用TLAB可以避免一系列的非线程安全问题，同时还能够提升内存分配的吞吐量，因此我们可以将这种内存分配方式称之为快速分配策略</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230315194821093.png" class="asset-class" title="image-20230315194821093">

<h4 id="TLAB的再说明"><a href="#TLAB的再说明" class="headerlink" title="TLAB的再说明"></a>TLAB的再说明</h4><ul>
<li>尽管不是所有的对象实例都能够在TLAB中成功分配内存，但 <strong>JVM确实是将TLAB作为内存分配的首选</strong></li>
<li>在程序中，开发人员可以通过选项 <code>-XX:UseTLAB</code> 设置是否开启TLAB空间</li>
<li>默认情况下，TLAB空间的内存非常小，仅占有整个Eden空间的1%，当然我们可以通过选项 <code>-XX:TLABWasteTargetPrecent</code>设置TLAB空间所占用Eden空间的百分比大小</li>
<li>一旦对象在TLAB空间分配内存失败时，JVM就会尝试着通过使用加锁机制确保数据操作的原子性，从而直接在Eden空间中分配内存</li>
</ul>
<img src="/2023/03/01/jvm-cong-ru-men-dao-jing-tong/image-20230315200128191.png" class="asset-class" title="image-20230315200128191">

<h3 id="堆空间的参数设置"><a href="#堆空间的参数设置" class="headerlink" title="堆空间的参数设置"></a>堆空间的参数设置</h3><p>官网地址：<a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html">https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html</a></p>
<pre><code class="prettyprint">-XX:+PrintFlagsInitial  //查看所有的参数的默认初始值
-XX:+PrintFlagsFinal  //查看所有的参数的最终值（可能会存在修改，不再是初始值）
-Xms  //初始堆空间内存（默认为物理内存的1/64）
-Xmx  //最大堆空间内存（默认为物理内存的1/4）
-Xmn  //设置新生代的大小。（初始值及最大值）
-XX:NewRatio  //配置新生代与老年代在堆结构的占比
-XX:SurvivorRatio  //设置新生代中Eden和S0/S1空间的比例
-XX:MaxTenuringThreshold  //设置新生代垃圾的最大年龄
-XX:+PrintGCDetails //输出详细的GC处理日志
//打印gc简要信息：①-Xx：+PrintGC ② - verbose:gc
-XX:HandlePromotionFalilure：//是否设置空间分配担保
</code></pre>
<p>在发生Minor GC之前，虚拟机会 <strong>检查老年代最大可用的连续空间是否大于新生代所有对象的总空间</strong></p>
<ul>
<li>如果大于，则此次Minor GC是安全的</li>
<li>如果小于，则虚拟机会查看 <code>-XX:HandlePromotionFailure</code>设置值是否设置空间分配担保<ul>
<li>如果 <code>-XX:HandlePromotionFalilure=true</code>，那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小<ul>
<li>如果大于，则尝试进行一次Minor GC,但这次Minor GC依然能是有风险的</li>
<li>如果小于，则改为进行一次Full GC</li>
</ul>
</li>
<li>如果 <code>-XX:HandlePromotionFalilure=false</code>，则改为进行一次Full GC</li>
</ul>
</li>
</ul>
<p>在JDK6 Update24之后，HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略，观察openJDK中的源码变化，虽然源码中还定义了HandlePromotionFailure参数，但是在代码中已经不会再使用它。JDK6 Update 24之后的规则变为<strong>只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC</strong>，否则将进行Full GC。</p>
<h3 id="堆时分配对象的唯一选择？"><a href="#堆时分配对象的唯一选择？" class="headerlink" title="堆时分配对象的唯一选择？"></a>堆时分配对象的唯一选择？</h3><p>早《深入理解java虚拟机》中关于java堆内存有这样一段描述：</p>
<blockquote>
<p>随着JIT编译器的发展与逃逸分析技术逐渐成熟，栈上分配、标量替换优化技术将会导致一些微妙的变化，所有的对象都分配到堆上也渐渐变得不那么“绝对”了</p>
</blockquote>
<p>在java虚拟机中，对象在java堆中分配内存的，这是一个普遍的常识。但是，有一种特殊情况，那就是如果经过逃逸分析（Escape Analysis）后发现，一个度一想并没有逃逸出方法的话，那么就可能被优化成栈上分配。这样就无需在堆上分配内存，也无须进行垃圾回收，这也是最常见的堆外存储技术</p>
<h4 id="逃逸分析概述"><a href="#逃逸分析概述" class="headerlink" title="逃逸分析概述"></a>逃逸分析概述</h4><p>如何将堆上的对象分配到栈，需要使用逃逸分析手段</p>
<p>这是一种可以有效减少java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法</p>
<p>通过逃逸分析，java HotSpot 编译器能够分析出一个新的对象引用的适用范围从而决定是否要将这个对象分配到堆上</p>
<p>逃逸分析的基本行为就是分析对象动态作用域</p>
<ul>
<li>当一个对像在方法中被定义后，对象只在方法内部使用，则认为没有发生逃逸</li>
<li>当一个对象在方法中被定义后，它被外部方法所引用，则认为发生逃逸。例如作为调用参数传递到其他地方中</li>
</ul>
<p><strong>举例1</strong></p>
<pre><code class="prettyprint">public void my_method() &#123;
    V v = new V();
    // use v
    // ....
    v = null;
&#125;
</code></pre>
<p>没有发生逃逸的对象，则可以分配到栈上，随着方法执行的结束，栈空间就被移除，每个栈里面包含了很多栈帧</p>
<pre><code class="prettyprint">public static StringBuffer createStringBuffer(String s1, String s2) &#123;
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb;
&#125;
</code></pre>
<p>上述方法如果想要<code>StringBuffer sb</code>不发生逃逸，可以这样写</p>
<pre><code class="prettyprint">public static String createStringBuffer(String s1, String s2) &#123;
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
&#125;
</code></pre>
<p><strong>举例2</strong></p>
<pre><code class="prettyprint">public class EscapeAnalysis &#123;

    public EscapeAnalysis obj;

    /**
     * 方法返回EscapeAnalysis对象，发生逃逸
     * @return
     */
    public EscapeAnalysis getInstance() &#123;
        return obj == null ? new EscapeAnalysis() : obj;
    &#125;

    /**
     * 为成员属性赋值，发生逃逸
     */
    public void setObj() &#123;
        this.obj = new EscapeAnalysis();
    &#125;

    /**
     * 对象的作用于仅在当前方法中有效，没有发生逃逸
     */
    public void useEscapeAnalysis() &#123;
        EscapeAnalysis e = new EscapeAnalysis();
    &#125;

    /**
     * 引用成员变量的值，发生逃逸
     */
    public void useEscapeAnalysis2() &#123;
        EscapeAnalysis e = getInstance();
    &#125;
&#125;
</code></pre>
<p><strong>参数设置</strong></p>
<p>在JDK 6u23 版本之后，HotSpot中默认就已经开启逃逸分析</p>
<p>如果使用的时较早的版本，开发人员则可以通过</p>
<ul>
<li>选项 <code>-XX:+DoEscapeAnalysis</code>显示开启逃逸分析</li>
<li>通过选项 <code>-XX:+PrintEscapeAnalysis</code>查看逃逸分析的筛选结果</li>
</ul>
<blockquote>
<p>开发中能使用局部变量的，就不要使用在方法外定义</p>
</blockquote>
<h4 id="逃逸分析：代码优化"><a href="#逃逸分析：代码优化" class="headerlink" title="逃逸分析：代码优化"></a>逃逸分析：代码优化</h4><p>使用逃逸分析，比一起可以堆代码做如下优化：</p>
<ul>
<li><strong>栈上分配：</strong>将堆分配转化为栈分配。如果一个对象在子程序中被分配，要使指向该对象的指针永远不会发生逃逸，对象可能是栈上分配的候选，而不是堆上分配</li>
<li><strong>同步省略：</strong>如果一个对象被发现只有一个线程被访问到，那么对于这个对象的操作可以不考虑同步。</li>
<li><strong>分离对象或标量替换：</strong>有的对象可能不需要作为一个连续的内存结构存在也可以被访问到，那么对象的部分（或全部）可以不存储在内存，而是存储在CPU寄存器中</li>
</ul>
<h5 id="栈上分配"><a href="#栈上分配" class="headerlink" title="栈上分配"></a>栈上分配</h5><p>JIT编译器在编译期间根据逃逸分析的结果，发现如果一个对象并没有逃逸出方法的话，就可能被优化成栈上分配。分配完成后，继续在调用栈内执行，最后线程结束，栈空间被回收，局部变量对象也被回收。这样就无须进行垃圾回收了。</p>
<p><strong>常见的栈上分配的场景</strong></p>
<p>在逃逸分析中，已经说明了。分别是给成员变量赋值、方法返回值、实例引用传递</p>
<h5 id="同步省略（锁消除）"><a href="#同步省略（锁消除）" class="headerlink" title="同步省略（锁消除）"></a>同步省略（锁消除）</h5><p>线程同步的代价是相当高的，同步的后果是降低并发性和性能</p>
<p>在动态编译同步块的时候，JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。如果没有，那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这样就能大大提高并发性和性能。这个取消同步的过程就叫同步省略，也叫锁消除。</p>
<p><strong>举例：</strong></p>
<pre><code class="prettyprint">public void f() &#123;
    Object hellis = new Object();
    synchronized(hellis) &#123;
        System.out.println(hellis);
    &#125;
&#125;
</code></pre>
<p>代码中堆hellis这个对象加锁，但是hellis对象的生命周期只在f()方法中，并不会被其他线程访问到，所以在JIT编译阶段就会被优化掉，优化成：</p>
<pre><code class="prettyprint">public void f() &#123;
    Object hellis = new Object();
    System.out.println(hellis);
&#125;
</code></pre>
<h5 id="标量替换"><a href="#标量替换" class="headerlink" title="标量替换"></a>标量替换</h5><p>**标量(scalar)**是指一个无法再分解成更小的数据的数据。java中原始数据类型就是标量</p>
<p>相对的，那些还可以分解的数据叫做**聚合量(Aggregate)**，java中的对象就是聚合量，因为他们可以分解成其他聚合量和标量</p>
<p>在JIT阶段，如果经过逃逸分析，发现一个对象不会被外界访问的话，那么经过JIT优化，就会把这个对象拆解成若干个成员变量来代替，这个过程就是标量替换。</p>
<p><strong>举例：</strong></p>
<pre><code class="prettyprint">public static void main(String args[]) &#123;
    alloc();
&#125;
private static void alloc() &#123;
    Point point = new Point(1,2);
    System.out.println(&quot;point.x&quot; + point.x + &quot;;point.y&quot; + point.y);
&#125;
class Point &#123;
    private int x;
    private int y;
&#125;
</code></pre>
<p>以上代码，经过标量替换后就会变成：</p>
<pre><code class="prettyprint">private static void alloc() &#123;
    int x = 1;
    int y = 2;
    System.out.println(&quot;point.x = &quot; + x + &quot;; point.y=&quot; + y);
&#125;
</code></pre>
<p>可以看到，Point这个聚合量经过逃逸分析后，发现它并没有逃逸，就被替换成两个标量了。那么标量替换有什么好处呢？就是可以大大减少堆内存的占用，因为一旦不需要创建对象了，那么就不在需要分配堆内存了。标量替换为栈上分配提供了很好的基础。</p>
<p><strong>标量替换参数设置</strong></p>
<p>上述代码在主函数中进行了1亿次alloc。调用进行对象创建，由于User对象实例需要占据约16字节的空间，因此累计分配空间达到将近1.5GB。如果堆空间小于这个值，就必然会发生GC。使用如下参数运行上述代码：</p>
<pre><code class="prettyprint">-server -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations
</code></pre>
<p>这里设置参数如下：</p>
<ul>
<li><p>参数<code>-server</code>：启动Server模式，因为在server模式下，才可以启用逃逸分析。</p>
</li>
<li><p>参数<code>-XX:+DoEscapeAnalysis</code>：启用逃逸分析</p>
</li>
<li><p>参数<code>-Xmx10m</code>：指定了堆空间最大为10MB</p>
</li>
<li><p>参数<code>-XX:+PrintGC</code>：将打印Gc日志</p>
</li>
<li><p>参数<code>-XX:+EliminateAllocations</code>：开启了标量替换（默认打开），允许将对象打散分配在栈上，比如对象拥有id和name两个字段，那么这两个字段将会被视为两个独立的局部变量进行分配</p>
</li>
</ul>
<h4 id="逃逸分析小结"><a href="#逃逸分析小结" class="headerlink" title="逃逸分析小结"></a>逃逸分析小结</h4><p>关于逃逸分析的论文在1999年就已经发表了，但直到JDK1.6才有实现，而且这项技术到如今也并不是十分成熟。</p>
<p>其根本原因就是无法保证逃逸分析的性能消耗一定能高于他的消耗。虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的，这其实也是一个相对耗时的过程。 </p>
<p>一个极端的例子，就是经过逃逸分析之后，发现没有一个对象是不逃逸的。那这个逃逸分析的过程就白白浪费掉了。</p>
<p>虽然这项技术并不十分成熟，但是它也是即时编译器优化技术中一个十分重要的手段。</p>
<p>注意到有一些观点，认为通过逃逸分析，JVM会在栈上分配那些不会逃逸的对象，这在理论上是可行的，但是取决于JVM设计者的选择。据我所知，Oracle Hotspot JVM中并未这么做，这一点在逃逸分析相关的文档里已经说明，所以可以明确所有的对象实例都是创建在堆上。</p>
<p>目前很多书籍还是基于JDK7以前的版本，JDK已经发生了很大变化，intern字符串的缓存和静态变量曾经都被分配在永久代上，而永久代已经被元数据区取代。但是，intern字符串缓存和静态变量并不是被转移到元数据区，而是直接在堆上分配，所以这一点同样符合前面一点的结论：对象实例都是分配在堆上。</p>
<blockquote>
<p>小结：</p>
<p>年轻代时对象的诞生、成长、消亡的区域，一个对象在这里产生、应用，最后被垃圾回收器收集、结束生命</p>
<p>老年代放置长生命周期的对象，通常都是从survivor区域筛选拷贝过来的java对象，当然，也有特殊情况，我们知道普通的对象会被分配在TLAB上；如果对象较大，JVM会试图直接分配在Eden其他位置上；如果对象太大，完全无法在新生代找到足够长的连续空闲空间，JVM就会直接分配到老年代。当GC只发生在年轻代中，回收年轻代对象的行为被称为Minor GC。</p>
<p>当GC发生在老年代时则被称为Major GC或者Full GC。一般的Minor GC发生的频率要比Major GC高很多，即老年代中垃圾回收发生的频率将大大低于年轻代。</p>
</blockquote>

      </section>

      
      
        <nav class="article-nav">
          
            <div class="article-nav-item layout-padding">
  <article class="card-container article-nav-card content-padding--primary soft-size--large soft-style--box">
    
      <div class="card-cover" background-image-lazy data-img="https://api.lixingyong.com/api/images?postid=bc2e7f08&type=url&itype=image"></div>
    
    <div class="card-text">
      
        <a href="/2023/03/02/typora-kuai-jie-jian-ji-lu/" itemprop="url">
          <h2 class="card-text--title text-ellipsis">typora快捷键记录</h2>
        </a>
      
      <div class="card-text--row">Newer</div>
    </div>
  </article>
</div>
          
          
            <div class="article-nav-item layout-padding">
  <article class="card-container article-nav-card content-padding--primary soft-size--large soft-style--box">
    
      <div class="card-cover" background-image-lazy data-img="https://api.lixingyong.com/api/images?postid=93f6fc65&type=url&itype=image"></div>
    
    <div class="card-text">
      
        <a href="/2023/02/25/yml-wen-jian-yu-fa-shuo-ming/" itemprop="url">
          <h2 class="card-text--title text-ellipsis">yml文件语法说明</h2>
        </a>
      
      <div class="card-text--row">Older</div>
    </div>
  </article>
</div>
          
        </nav>
      

      <section class="page-message-container layout-padding">
        


  
  
    <div class="valine-container comments-container content-padding--primary soft-size--large soft-style--box">
      <div id="valine_thread" class="valine-thread"></div>
    </div>
    <script type="text/javascript" src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
    <script type="text/javascript" src='//unpkg.com/valine/dist/Valine.min.js'></script>
    <script type="text/javascript">
      new Valine({
        el: "#valine_thread",
        appId: "yEts8yIOQopn7imcDAmwhlNi-gzGzoHsz",
        appKey: "nyOyHJdsDGQh3UI0TdNdLLnN",
        avatar: "mm",
        placeholder: "随便说点什么叭～",
        notify: true,
        visitor: true,
        pageSize: 10,
      });
    </script>
  

  
  


      </section>
    </div>
    <div class="widget-info">
      <section class="widget-calendar widget-item layout-margin content-padding--primary soft-size--large soft-style--box">
  <div class="widget-body">
	<div id="calendar"></div>
  </div>
</section>

      <section class="widget-categorys widget-item layout-margin content-padding--primary soft-size--large soft-style--box">
  <div class="widget-title">
    <svg class="icon icon-categories" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M900.3614811 257.09082106h-339.81629553l-67.96326003-101.9448889c-19.41807444-29.12711113-48.54518557-43.69066667-82.52681443-43.69066667H123.6385189c-53.39970333 0-97.09036999 43.69066667-97.09037113 97.09036999v582.54222222c0 53.39970333 43.69066667 97.09036999 97.09037113 97.09037002h776.7229622c53.39970333 0 97.09036999-43.69066667 97.09037113-97.09037002V354.18119104c0-53.39970333-43.69066667-97.09036999-97.09037113-97.09036998z m-97.09036999 242.72592554H220.72888889c-24.27259221 0-48.54518557-24.27259221-48.54518556-48.54518556s24.27259221-48.54518557 48.54518556-48.54518444h582.54222222c24.27259221 0 48.54518557 24.27259221 48.54518556 48.54518444s-24.27259221 48.54518557-48.54518556 48.54518556z" fill="currentColor"></path>
</svg>
    <span>CATEGORYS</span>
  </div>
  <div class="widget-body">
    <ul class="categorys-list">
      
        <li class="categorys-list-item">
          <a href="/categories/%E6%8A%80%E6%9C%AF/">
            技术 (11)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E7%AE%97%E6%B3%95/">
            算法 (15)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E5%B7%A5%E5%85%B7/">
            工具 (3)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E9%97%AE%E9%A2%98/">
            问题 (8)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E8%BD%AF%E4%BB%B6/">
            软件 (31)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/">
            中间件 (26)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E5%90%8E%E7%AB%AF/">
            后端 (1)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/java/">
            java (4)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E6%95%B0%E6%8D%AE/">
            数据 (5)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/linux/">
            linux (3)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/web/">
            web (1)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E7%89%9B%E8%B7%AF%E6%9D%91/">
            牛路村 (1)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E9%97%B2%E8%B0%88/">
            闲谈 (5)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E5%89%8D%E7%AB%AF/">
            前端 (1)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E5%BE%AE%E4%BF%A1/">
            微信 (1)
          </a>
        </li>
      
        <li class="categorys-list-item">
          <a href="/categories/%E8%8B%A5%E4%BE%9D/">
            若依 (1)
          </a>
        </li>
      
    </ul>
  </div>
</section>

      <section class="widget-tags widget-item  layout-margin content-padding--primary soft-size--large soft-style--box">
  <div class="widget-title">
    <svg class="icon icon-tags" viewBox="0 0 1098 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M283.42180005 272q0-28.38857157-20.09142843-48.48000001t-48.47999998-20.09142842-48.48000002 20.09142842-20.09142846 48.48000001 20.09142846 48.48 48.48000002 20.09142843 48.47999998-20.09142843 20.09142843-48.48zM855.0332285 580.57142843q0 28.38857157-19.81714313 48.2057147l-263.03999997 263.58857157q-20.9142853 19.81714313-48.75428534 19.81714312-28.38857157 0-48.20571468-19.81714312l-383.04-383.58857157q-20.36571468-19.81714313-34.55999999-54.10285688t-14.19428534-62.6742853l0-222.85714313q0-27.84000002 20.36571469-48.20571469t48.2057147-20.36571466l222.85714313 0q28.38857157 0 62.6742853 14.19428529t54.65142842 34.55999999l383.04000001 382.49142843q19.81714313 20.9142853 19.81714314 48.75428532zM1060.74751475 580.57142843q0 28.38857157-19.81714313 48.2057147l-263.04 263.58857157q-20.9142853 19.81714313-48.75428531 19.81714312-19.26857155 0-31.61142843-7.47428531t-28.38857159-24.13714314l251.79428534-251.7942853q19.81714313-19.81714313 19.81714308-48.20571469 0-27.84000002-19.81714308-48.75428531l-383.04000001-382.49142845q-20.36571468-20.36571468-54.65142842-34.55999999t-62.67428532-14.19428534l120 0q28.38857157 0 62.67428532 14.19428534t54.65142842 34.55999999l383.03999998 382.49142845q19.81714313 20.9142853 19.81714314 48.75428531z" fill="currentColor"></path>
</svg>
    <span>TAGS</span>
  </div>
  <div class="widget-body">
    <div class="tags-cloud">
      <a href="/tags/crontab/" style="font-size: 10px;" class="tags-cloud-0">crontab</a> <a href="/tags/docker/" style="font-size: 20px;" class="tags-cloud-10">docker</a> <a href="/tags/elasticsearch/" style="font-size: 10px;" class="tags-cloud-0">elasticsearch</a> <a href="/tags/fastdfs/" style="font-size: 10px;" class="tags-cloud-0">fastdfs</a> <a href="/tags/git/" style="font-size: 10px;" class="tags-cloud-0">git</a> <a href="/tags/go/" style="font-size: 12.5px;" class="tags-cloud-3">go</a> <a href="/tags/hexo/" style="font-size: 10px;" class="tags-cloud-0">hexo</a> <a href="/tags/idea/" style="font-size: 10px;" class="tags-cloud-0">idea</a> <a href="/tags/jvm/" style="font-size: 11.25px;" class="tags-cloud-1">jvm</a> <a href="/tags/kafka/" style="font-size: 11.25px;" class="tags-cloud-1">kafka</a> <a href="/tags/linux/" style="font-size: 15px;" class="tags-cloud-5">linux</a> <a href="/tags/maven/" style="font-size: 10px;" class="tags-cloud-0">maven</a> <a href="/tags/minio/" style="font-size: 11.25px;" class="tags-cloud-1">minio</a> <a href="/tags/mysql/" style="font-size: 13.75px;" class="tags-cloud-4">mysql</a> <a href="/tags/nginx/" style="font-size: 12.5px;" class="tags-cloud-3">nginx</a> <a href="/tags/node/" style="font-size: 10px;" class="tags-cloud-0">node</a> <a href="/tags/oracle/" style="font-size: 10px;" class="tags-cloud-0">oracle</a> <a href="/tags/pulsar/" style="font-size: 10px;" class="tags-cloud-0">pulsar</a> <a href="/tags/rabbitmq/" style="font-size: 17.5px;" class="tags-cloud-8">rabbitmq</a> <a href="/tags/redis/" style="font-size: 16.25px;" class="tags-cloud-6">redis</a> <a href="/tags/redis-%E4%BA%8B%E5%8A%A1/" style="font-size: 10px;" class="tags-cloud-0">redis,事务</a> <a href="/tags/rocketmq/" style="font-size: 17.5px;" class="tags-cloud-8">rocketmq</a> <a href="/tags/shell/" style="font-size: 10px;" class="tags-cloud-0">shell</a> <a href="/tags/spring/" style="font-size: 10px;" class="tags-cloud-0">spring</a> <a href="/tags/tomcat/" style="font-size: 10px;" class="tags-cloud-0">tomcat</a> <a href="/tags/vim/" style="font-size: 10px;" class="tags-cloud-0">vim</a> <a href="/tags/yml/" style="font-size: 10px;" class="tags-cloud-0">yml</a> <a href="/tags/zookeeper/" style="font-size: 10px;" class="tags-cloud-0">zookeeper</a> <a href="/tags/%E4%B8%87%E5%B9%B4%E5%8F%B0/" style="font-size: 10px;" class="tags-cloud-0">万年台</a> <a href="/tags/%E4%B8%AD%E9%97%B4%E4%BB%B6/" style="font-size: 18.75px;" class="tags-cloud-9">中间件</a> <a href="/tags/%E4%BA%8B%E5%8A%A1/" style="font-size: 10px;" class="tags-cloud-0">事务</a> <a href="/tags/%E5%8A%A0%E5%AF%86/" style="font-size: 13.75px;" class="tags-cloud-4">加密</a> <a href="/tags/%E5%93%A8%E5%85%B5/" style="font-size: 10px;" class="tags-cloud-0">哨兵</a> <a href="/tags/%E5%B7%A5%E5%85%B7/" style="font-size: 11.25px;" class="tags-cloud-1">工具</a> <a href="/tags/%E5%B8%B8%E7%94%A8%E7%AE%97%E6%B3%95/" style="font-size: 16.25px;" class="tags-cloud-6">常用算法</a> <a href="/tags/%E5%B9%B6%E5%8F%91/" style="font-size: 10px;" class="tags-cloud-0">并发</a> <a href="/tags/%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8/" style="font-size: 11.25px;" class="tags-cloud-1">文件存储</a> <a href="/tags/%E7%B4%A2%E5%BC%95/" style="font-size: 10px;" class="tags-cloud-0">索引</a> <a href="/tags/%E9%94%81%E6%9C%BA%E5%88%B6/" style="font-size: 10px;" class="tags-cloud-0">锁机制</a> <a href="/tags/%E9%97%AE%E9%A2%98/" style="font-size: 10px;" class="tags-cloud-0">问题</a>
    </div>
  </div>
</section>
    </div>
  </article>
</div>
<script>
	//解决中文转义无法跳转锚点问题
	var blog = document.querySelector("#blog-content");
	var content = blog.innerHTML;
	var regExp2 = /<h([1-6]) id=\"(\S*?)\">/g;
	var	data = content.replace(regExp2, function(word,i){
		var ws = word.split("\"");
			return ws[0]+"\""+encodeURIComponent(ws[1])+"\""+ws[2];
		},"g");
	blog.innerHTML = data;
</script>

    <!-- footer container -->
<footer id="footer" class="footer">
  <div class="footer-container">
    
    <div class="social-icons">
      
        
          <a href="https://music.163.com/#/user/home?id=444731906" class="soft-size--primary soft-style--box" target="_blank" rel="noopener noreferrer">
            <svg class="icon icon-<%= icon %>" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M952.17867 503.263237a341.054956 341.054956 0 0 0-56.853199-113.706399 408.604266 408.604266 0 0 0-109.723463-98.352823 304.662484 304.662484 0 0 0-143.257215-42.077792L621.305897 170.238394a40.343287 40.343287 0 0 1 10.792472-36.392472 56.88532 56.88532 0 0 1 36.392472-18.758344 102.785445 102.785445 0 0 1 56.853199 18.758344c6.263488 3.404768 10.792472 9.090088 17.05596 13.073024a57.335006 57.335006 0 0 0 82.999247-70.632873 66.521455 66.521455 0 0 0-22.74128-27.848432 237.691343 237.691343 0 0 0-89.262735-43.202007A170.559598 170.559598 0 0 0 625.288833 6.42409a167.026349 167.026349 0 0 0-60.836135 37.516688 164.328231 164.328231 0 0 0-40.921456 51.713927 156.908407 156.908407 0 0 0-8.54404 113.706399l13.651192 52.870264a284.298118 284.298118 0 0 0-158.064743 109.209535 256.963614 256.963614 0 0 0-43.202007 156.908407 248.419573 248.419573 0 0 0 13.651192 74.487327 205.281807 205.281807 0 0 0 80.718695 104.616312 185.945295 185.945295 0 0 0 136.447679 25.696361 193.879046 193.879046 0 0 0 156.330238-196.705646L708.802008 503.263237c-11.948808-46.060728-24.443664-91.543287-35.814304-137.025847a182.283563 182.283563 0 0 1 47.763112 18.180176 316.611292 316.611292 0 0 1 47.763111 34.690088A239.907654 239.907654 0 0 1 840.752823 529.987453a284.169636 284.169636 0 0 1 0 158.064743 252.241907 252.241907 0 0 1-21.584943 51.167879 308.741782 308.741782 0 0 1-168.279047 144.542033 387.918695 387.918695 0 0 1-130.184191 23.319448 332.607277 332.607277 0 0 1-155.206022-37.516688A367.233124 367.233124 0 0 1 194.87478 672.666499a373.014806 373.014806 0 0 1-20.460727-180.806022A366.173149 366.173149 0 0 1 406.869762 198.536512a57.399247 57.399247 0 0 0 13.073023-100.055207 76.189711 76.189711 0 0 0-63.662735-3.404768A479.815307 479.815307 0 0 0 68.705646 436.163614a488.905395 488.905395 0 0 0 129.027855 450.84266 454.857716 454.857716 0 0 0 248.997741 131.886574 470.885822 470.885822 0 0 0 138.117942 0A454.889837 454.889837 0 0 0 773.042911 952.949561a427.523212 427.523212 0 0 0 170.559598-209.232622 417.276788 417.276788 0 0 0 22.74128-109.209536 378.089837 378.089837 0 0 0-14.165119-131.244166z m-412.747804 118.845671a72.174655 72.174655 0 0 1-56.8532-10.792471 98.352823 98.352823 0 0 1-37.516687-61.382184 170.68808 170.68808 0 0 1 0-65.943287 133.042911 133.042911 0 0 1 33.533751-63.662735 181.35207 181.35207 0 0 1 80.718695-46.606776l39.219072 148.364367a204.189711 204.189711 0 0 1 5.107152 26.146048 79.594479 79.594479 0 0 1-64.208783 73.844918z"></path>
  </svg>
          </a>
        
      
        
          <a href="https://www.zhihu.com" class="soft-size--primary soft-style--box" target="_blank" rel="noopener noreferrer">
            <svg t="1587373160860" class="icon icon-zhihu" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M544.949 561.422s0-71.387-34.779-75.050c-34.779-3.663-142.775 0-142.775 0v-219.654h161.078s-1.83-73.219-32.949-73.219h-261.755l43.93-117.148s-65.897 3.663-89.692 45.761-98.844 252.604-98.844 252.604 25.627 10.983 67.726-20.134c42.101-31.116 56.743-86.033 56.743-86.033l76.879-3.663 1.83 223.316s-133.621-1.83-161.078 0c-27.457 1.83-42.101 75.050-42.101 75.050h203.182s-18.307 124.47-69.557 214.164c-53.085 89.692-151.929 161.078-151.929 161.078s71.387 29.287 140.947-10.983c69.557-42.101 120.811-223.316 120.811-223.316l162.912 203.182s14.643-97.013-1.83-124.47c-18.307-27.457-113.49-137.283-113.49-137.283l-42.101 36.607 29.287-120.811h177.552zM587.050 188.010l-1.83 660.793h65.897l23.795 82.37 115.321-82.37h162.912v-660.793h-366.091zM879.92 775.584h-76.879l-97.013 75.050-21.965-75.050h-20.134v-512.527h215.991v512.527z"></path>
</svg>
          </a>
        
      
        
          <a href="https://gitee.com/RocPengHua" class="soft-size--primary soft-style--box" target="_blank" rel="noopener noreferrer">
            <svg class="icon icon-<%= icon %>" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <path d="M512 1024C229.248 1024 0 794.752 0 512S229.248 0 512 0s512 229.248 512 512-229.248 512-512 512z m259.168-568.896h-290.752a25.28 25.28 0 0 0-25.28 25.28l-0.032 63.232c0 13.952 11.296 25.28 25.28 25.28h177.024a25.28 25.28 0 0 1 25.28 25.28v12.64a75.84 75.84 0 0 1-75.84 75.84h-240.224a25.28 25.28 0 0 1-25.28-25.28v-240.192a75.84 75.84 0 0 1 75.84-75.84h353.92a25.28 25.28 0 0 0 25.28-25.28l0.064-63.2a25.312 25.312 0 0 0-25.28-25.312H417.184a189.632 189.632 0 0 0-189.632 189.6v353.952c0 13.952 11.328 25.28 25.28 25.28h372.928a170.656 170.656 0 0 0 170.656-170.656v-145.376a25.28 25.28 0 0 0-25.28-25.28z" p-id="1954"></path>
  </svg>
          </a>
        
      
        
          <a href="https://github.com/RocPengHua" class="soft-size--primary soft-style--box" target="_blank" rel="noopener noreferrer">
            <svg class="icon icon-github" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M64.6 512c0 195.6 125.4 361.9 300.1 422.9 23.5 5.9 19.9-10.8 19.9-22.2v-77.6c-135.8 15.9-141.3-74-150.5-89-18.5-31.5-61.9-39.5-49-54.5 31-15.9 62.5 4 98.9 58 26.4 39.1 77.9 32.5 104.1 26 5.7-23.5 17.9-44.5 34.7-60.9-140.7-25.2-199.4-111.1-199.4-213.3 0-49.5 16.4-95.1 48.4-131.8-20.4-60.6 1.9-112.4 4.9-120.1 58.2-5.2 118.5 41.6 123.3 45.3 33.1-8.9 70.8-13.7 112.9-13.7 42.4 0 80.3 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.4-43.9 2.9 7.7 24.7 58.3 5.5 118.1 32.5 36.8 49 82.8 49 132.4 0 102.3-59 188.3-200.2 213.2 23.5 23.3 38.1 55.5 38.1 91.1v112.7c0.8 9 0 17.9 15.1 17.9C832.7 877 960.4 709.4 960.4 512.1c0-247.5-200.6-447.9-447.9-447.9C265 64.1 64.6 264.5 64.6 512z"></path>
</svg>
          </a>
        
      
        
          <a href="https://twitter.com/guanquanhong" class="soft-size--primary soft-style--box" target="_blank" rel="noopener noreferrer">
            <svg class="icon icon-twitter" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M962.285714 233.142857q-38.285714 56-92.571429 95.428571 0.571429 8 0.571429 24 0 74.285714-21.714286 148.285714t-66 142-105.428571 120.285714-147.428571 83.428571-184.571429 31.142857q-154.857143 0-283.428571-82.857143 20 2.285714 44.571429 2.285714 128.571429 0 229.142857-78.857143-60-1.142857-107.428571-36.857143t-65.142857-91.142857q18.857143 2.857143 34.857143 2.857143 24.571429 0 48.571429-6.285714-64-13.142857-106-63.714286t-42-117.428571l0-2.285714q38.857143 21.714286 83.428571 23.428571-37.714286-25.142857-60-65.714286t-22.285714-88q0-50.285714 25.142857-93.142857 69.142857 85.142857 168.285714 136.285714t212.285714 56.857143q-4.571429-21.714286-4.571429-42.285714 0-76.571429 54-130.571429t130.571429-54q80 0 134.857143 58.285714 62.285714-12 117.142857-44.571429-21.142857 65.714286-81.142857 101.714286 53.142857-5.714286 106.285714-28.571429z"></path>
</svg>
          </a>
        
      
    </div>
     
    <p>&copy; 2024 <a href="/" target="_blank">RocPengHua</a></p>

    

    <p>Powered by <a href="https://hexo.io" target="_blank" rel="noopener noreferrer">Hexo</a> Theme - <a href="https://github.com/miiiku/flex-block" target="_blank" rel="noopener noreferrer author">flex-block</a></p>

    <p>
      <a href="javascript:;" id="theme-light">🌞 浅色</a>
      <a href="javascript:;" id="theme-dark">🌛 深色</a>
      <a href="javascript:;" id="theme-auto">🤖️ 自动</a>
    </p>
  </div>
</footer>
  </div>

  <div class="back-to-top-fixed soft-size--round soft-style--box">
    <svg class="icon icon-back-to-top" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
      <path d="M725.333333 426.666667c-12.8 0-21.333333-4.266667-29.866667-12.8l-213.333333-213.333333c-17.066667-17.066667-17.066667-42.666667 0-59.733333s42.666667-17.066667 59.733333 0l213.333333 213.333333c17.066667 17.066667 17.066667 42.666667 0 59.733333C746.666667 422.4 738.133333 426.666667 725.333333 426.666667z"></path>
      <path d="M298.666667 426.666667c-12.8 0-21.333333-4.266667-29.866667-12.8-17.066667-17.066667-17.066667-42.666667 0-59.733333l213.333333-213.333333c17.066667-17.066667 42.666667-17.066667 59.733333 0s17.066667 42.666667 0 59.733333l-213.333333 213.333333C320 422.4 311.466667 426.666667 298.666667 426.666667z"></path>
      <path d="M512 896c-25.6 0-42.666667-17.066667-42.666667-42.666667L469.333333 170.666667c0-25.6 17.066667-42.666667 42.666667-42.666667s42.666667 17.066667 42.666667 42.666667l0 682.666667C554.666667 878.933333 537.6 896 512 896z"></path>
    </svg>
  </div>

  
  <!-- aplayer -->


<!-- dplayer -->




  


  


  <!-- Baidu Analytics START -->
  <script>
    var _hmt = _hmt || [];
    (function () {
      if (window.location.hostname === "localhost" || window.location.hostname.startsWith("192.168")) {
        return console.log("本地调试");
      }
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?true";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>
  <!-- Baidu Analytics End -->

  




<script src="/js/script.js"></script>


  
  <link rel="stylesheet" href="/lib/aplayer/APlayer.min.css">
<script type="text/javascript" src="/lib/aplayer/APlayer.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="/js/search.js"></script>

<!-- Calendar -->

    <script src="/js/calendar.js"></script>
    <script src="/js/languages.js"></script>
    <script type="text/javascript">
        $(function() {
            $('#calendar').aCalendar(
                'zh-CN',
                {single:false, root:'calendar/'}
            );
        });
    </script>

<!-- 尾部用户自定义相关内容 -->
<div id="aplayer"></div>
<script type="text/javascript">

	/*获取音乐*/
	$.get("https://api.i-meto.com/meting/api?server=netease&type=playlist&id=3778678&r=0.37201068616290645", function(result){
		if(result && result.code  == 50000){
			console.log(result.msg);
			return;
		}
	    const ap = new APlayer({
	    container: document.getElementById('aplayer'),
	    global:true,
	    id:'3778678',
	    server: 'netease',
	    type: 'paylist',
	    fixed: true,
	    preload: 'none',
	    theme: '#ffa500',
	    autoplay: false,
	    audio: result
	  });
	});










	/*图片放大js*/
	function createImgEventFullScreen() {
        var imgs = $(".post-container").find("img");
        for (var i = 0; i < imgs.length; i++) {
            // $(imgs[i]).click(createCover(imgs[i]));
            imgs[i].onclick = function (e) {
                var src = e.srcElement.currentSrc;
                var _this = $(this);
                console.log(_this);
                createCover(src,_this);
            }
        }
        function createCover(src,_this) {
            console.log(_this);
            console.log(src);
            var cover = $("<div id='outerDiv'  style='position:fixed;top:0;left:0;background:rgba(0,0,0,0.7);z-index:5;width:100%;height:100%;display:none;'><div id='innerDiv' style='position:absolute;'><img  id='bigImg' style='border:5px solid #fff;' src=''/></div></div>");
            $("#outerDiv").remove();
            $("body").append(cover);
            imgShow("#outerDiv", "#innerDiv", "#bigImg", _this,src);

        }
    }
    function imgShow(outerDiv, innerDiv, bigImg, _this,src) {
        //var src = _this.attr("src"); //获取当前点击的common-img元素中的src属性
        $(bigImg).attr("src", src); //设置#bigImg元素的src属性

        /*获取当前点击图片的真实大小，并显示弹出层及大图*/
        $("<img/>").attr("src", src).on('load',function () {
            var windowW = $(window).width(); //获取当前窗口宽度
            var windowH = $(window).height(); //获取当前窗口高度
            var realWidth = this.width; //获取图片真实宽度
            var realHeight = this.height; //获取图片真实高度
            var imgWidth, imgHeight;
            var scale = 0.8; //缩放尺寸，当图片真实宽度和高度大于窗口宽度和高度时进行缩放

            if (realHeight > windowH * scale) { //判断图片高度
                imgHeight = windowH * scale; //如大于窗口高度，图片高度进行缩放
                imgWidth = imgHeight / realHeight * realWidth; //等比例缩放宽度
                if (imgWidth > windowW * scale) { //如宽度仍大于窗口宽度
                    imgWidth = windowW * scale; //再对宽度进行缩放
                }
            } else if (realWidth > windowW * scale) { //如图片高度合适，判断图片宽度
                imgWidth = windowW * scale; //如大于窗口宽度，图片宽度进行缩放
                imgHeight = imgWidth / realWidth * realHeight; //等比例缩放高度
            } else { //如果图片真实高度和宽度都符合要求，高宽不变
                imgWidth = realWidth;
                imgHeight = realHeight;
            }
            $(bigImg).css("width", imgWidth); //以最终的宽度对图片缩放

            var w = (windowW - imgWidth) / 2; //计算图片与窗口左边距
            var h = (windowH - imgHeight) / 2; //计算图片与窗口上边距
            $(innerDiv).css({ "top": h, "left": w }); //设置#innerDiv的top和left属性
            //console.log('****')
            $(outerDiv).fadeIn("fast"); //淡入显示#outerDiv
        });

        $(outerDiv).click(function () { //再次点击淡出消失弹出层
            $(this).fadeOut("fast");
        });
    }
    setTimeout(function () {
        createImgEventFullScreen();
    }, 1000)
</script>
<style>
.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body{
	width: 0px!important
}
.aplayer.aplayer-narrow .aplayer-body, .aplayer.aplayer-narrow .aplayer-pic {
	width: 0px!important
}
</style>
</body>
</html>